From fa38f4d025032cd87f45381b2564e9fa29b1cbad Mon Sep 17 00:00:00 2001
From: Jack Flintermann <jflinter11@gmail.com>
Date: Fri, 14 Apr 2017 13:58:23 -0400
Subject: [PATCH 01/48] podspec

---
 Dwifft.podspec | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Dwifft.podspec b/Dwifft.podspec
index ea12ce4..4a1a1c6 100644
--- a/Dwifft.podspec
+++ b/Dwifft.podspec
@@ -1,12 +1,12 @@
 Pod::Spec.new do |s|
   s.name = 'Dwifft'
-  s.version = '0.5'
+  s.version = '0.6'
   s.license = 'MIT'
   s.summary = 'Swift Diff'
   s.homepage = 'https://github.com/jflinter/Dwifft'
   s.social_media_url = 'http://twitter.com/jflinter'
-  s.author = "Jack Flintermann"
-  s.source = { :git => 'https://github.com/jflinter/Dwifft.git', :tag => s.version }
+  s.author = 'Jack Flintermann'
+  s.source = { git: 'https://github.com/jflinter/Dwifft.git', tag: s.version }
 
   s.ios.deployment_target = '8.0'
   s.tvos.deployment_target = '9.0'

From 8ae1e8f800194804c2636a04ec4ff6224e743b90 Mon Sep 17 00:00:00 2001
From: Jack Flintermann <jflinter11@gmail.com>
Date: Mon, 17 Apr 2017 12:34:27 -0400
Subject: [PATCH 02/48] update scheme

---
 Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft.xcscheme | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft.xcscheme b/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft.xcscheme
index 6f78b41..d35c757 100644
--- a/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft.xcscheme
+++ b/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft.xcscheme
@@ -22,10 +22,10 @@
          </BuildActionEntry>
          <BuildActionEntry
             buildForTesting = "YES"
-            buildForRunning = "YES"
+            buildForRunning = "NO"
             buildForProfiling = "NO"
             buildForArchiving = "NO"
-            buildForAnalyzing = "YES">
+            buildForAnalyzing = "NO">
             <BuildableReference
                BuildableIdentifier = "primary"
                BlueprintIdentifier = "041EBB9C1B89679200E113B3"

From d3fa85aedf15e3a10593d11c93771c5657324289 Mon Sep 17 00:00:00 2001
From: Jack Flintermann <jflinter11@gmail.com>
Date: Mon, 17 Apr 2017 14:44:38 -0400
Subject: [PATCH 03/48] podspec

---
 Dwifft.podspec | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Dwifft.podspec b/Dwifft.podspec
index 4a1a1c6..1faf1b3 100644
--- a/Dwifft.podspec
+++ b/Dwifft.podspec
@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name = 'Dwifft'
-  s.version = '0.6'
+  s.version = '0.6.1'
   s.license = 'MIT'
   s.summary = 'Swift Diff'
   s.homepage = 'https://github.com/jflinter/Dwifft'

From 7f98e3e31e60bce3368c88386cf08b6cae3c47e6 Mon Sep 17 00:00:00 2001
From: Maximus <mxmccann@gmail.com>
Date: Mon, 17 Apr 2017 17:02:07 -0500
Subject: [PATCH 04/48] Set SingleSectionTableViewDiffCalculator animations.

---
 Dwifft/Dwifft+UIKit.swift | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/Dwifft/Dwifft+UIKit.swift b/Dwifft/Dwifft+UIKit.swift
index 88fb54a..1af4cb6 100644
--- a/Dwifft/Dwifft+UIKit.swift
+++ b/Dwifft/Dwifft+UIKit.swift
@@ -181,7 +181,17 @@ public final class SingleSectionTableViewDiffCalculator<Value: Equatable> {
 
     /// You can change insertion/deletion animations like this! Fade works well.
     /// So does Top/Bottom. Left/Right/Middle are a little weird, but hey, do your thing.
-    public var insertionAnimation = UITableViewRowAnimation.automatic, deletionAnimation = UITableViewRowAnimation.automatic
+    public var insertionAnimation = UITableViewRowAnimation.automatic {
+        didSet {
+            self.internalDiffCalculator.insertionAnimation = self.insertionAnimation 
+        }
+    }
+    
+    public var deletionAnimation = UITableViewRowAnimation.automatic {
+        didSet {
+            self.internalDiffCalculator.deletionAnimation = self.deletionAnimation 
+        }
+    }
 
     /// Set this variable to automatically trigger the correct row insertion/deletions
     /// on your table view.

From bda2e83bda52f804a0afc021fe01b46fdd54aae9 Mon Sep 17 00:00:00 2001
From: Jack Flintermann <jflinter11@gmail.com>
Date: Thu, 20 Apr 2017 10:59:09 -0700
Subject: [PATCH 05/48] 0.6.2

---
 Dwifft.podspec            | 2 +-
 Dwifft/Dwifft+UIKit.swift | 4 +++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/Dwifft.podspec b/Dwifft.podspec
index 1faf1b3..612c9cb 100644
--- a/Dwifft.podspec
+++ b/Dwifft.podspec
@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name = 'Dwifft'
-  s.version = '0.6.1'
+  s.version = '0.6.2'
   s.license = 'MIT'
   s.summary = 'Swift Diff'
   s.homepage = 'https://github.com/jflinter/Dwifft'
diff --git a/Dwifft/Dwifft+UIKit.swift b/Dwifft/Dwifft+UIKit.swift
index 1af4cb6..018f016 100644
--- a/Dwifft/Dwifft+UIKit.swift
+++ b/Dwifft/Dwifft+UIKit.swift
@@ -10,7 +10,9 @@
 
 import UIKit
 
-internal class AbstractDiffCalculator<Section: Equatable, Value: Equatable> {
+
+/// A parent class for all diff calculators. Don't use it directly.
+public class AbstractDiffCalculator<Section: Equatable, Value: Equatable> {
 
     fileprivate init(initialSectionedValues: SectionedValues<Section, Value>) {
         self._sectionedValues = initialSectionedValues

From 5c0095a25921dc9971691c6fa27ca24cc2297f82 Mon Sep 17 00:00:00 2001
From: Jack Flintermann <jflinter11@gmail.com>
Date: Thu, 4 May 2017 00:58:10 -0400
Subject: [PATCH 06/48] package.swift

---
 .gitignore    |  1 +
 Package.swift | 14 ++++++++++++++
 2 files changed, 15 insertions(+)
 create mode 100644 Package.swift

diff --git a/.gitignore b/.gitignore
index 6749b45..039109e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
 ### Swift ###
 
 ## Build generated
+.build/
 build/
 DerivedData
 
diff --git a/Package.swift b/Package.swift
new file mode 100644
index 0000000..d0d14aa
--- /dev/null
+++ b/Package.swift
@@ -0,0 +1,14 @@
+import PackageDescription
+
+let package = Package(
+    name: "Dwifft",
+    dependencies : [],
+    exclude: [
+        "Carthage",
+        "DwifftTests",
+        "DwifftExample",
+        "docs",
+        "Dwifft.xcworkspace",
+        "scripts",
+    ]
+)

From e3e8bf0d2355a23dc0f1af3a0b6465efcb3b6488 Mon Sep 17 00:00:00 2001
From: Jack Flintermann <jflinter11@gmail.com>
Date: Thu, 4 May 2017 01:14:15 -0400
Subject: [PATCH 07/48] remove unnecessary state update

---
 Dwifft/Dwifft+UIKit.swift | 1 -
 1 file changed, 1 deletion(-)

diff --git a/Dwifft/Dwifft+UIKit.swift b/Dwifft/Dwifft+UIKit.swift
index 018f016..3498ec3 100644
--- a/Dwifft/Dwifft+UIKit.swift
+++ b/Dwifft/Dwifft+UIKit.swift
@@ -142,7 +142,6 @@ public final class CollectionViewDiffCalculator<Section: Equatable, Value: Equat
 
     override fileprivate func processChanges(newState: SectionedValues<Section, Value>, diff: [SectionedDiffStep<Section, Value>]) {
         guard let collectionView = self.collectionView else { return }
-        self._sectionedValues = newState
         collectionView.performBatchUpdates({
             self._sectionedValues = newState
             for result in diff {

From 92b47cbf134aa2eb47ab932f306dbee3e69f2312 Mon Sep 17 00:00:00 2001
From: Jan Gorman <gorman.jan@gmail.com>
Date: Mon, 19 Jun 2017 16:46:56 +0200
Subject: [PATCH 08/48] UIKit extension and travis build matrix

---
 .travis.yml               | 32 ++++++++++++++++++++++++++++----
 Dwifft/Dwifft+UIKit.swift |  2 +-
 2 files changed, 29 insertions(+), 5 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 881e2bd..42a33da 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,31 @@
-osx_image: xcode8
-language: objective-c
-before_script: carthage bootstrap --platform ios --configuration Debug
-script: set -o pipefail && xcodebuild clean build build-for-testing  -project Dwifft.xcodeproj -scheme Dwifft -sdk iphonesimulator10.0 -destination 'platform=iOS Simulator,name=iPhone 7' | xcpretty -c && xcodebuild test-without-building -project Dwifft.xcodeproj -scheme Dwifft -sdk iphonesimulator10.0 -destination 'platform=iOS Simulator,name=iPhone 7' | xcpretty -c
+matrix:
+  include:
+    - os: osx
+      language: objective-c
+      osx_image: xcode8
+      env:
+        - PLATFORM=iOS NAME='iPhone 7'
+      before_install:
+        export UUID=$(instruments -s | ruby -e "ARGF.each_line{ |ln| ln =~ /$NAME .* \[(.*)\]/; if \$1; puts(\$1); exit; end }");
+      before_script:
+        carthage bootstrap --platform $PLATFORM --configuration Debug
+      script:
+        - set -o pipefail;
+          open -a "simulator" --args -CurrentDeviceUDID "$UUID"
+          xcodebuild -scheme Dwifft -destination "id=$UUID" test | xcpretty;
+    - os: osx
+      language: objective-c
+      osx_image: xcode8
+      env:
+        - PLATFORM=tvOS NAME='Apple TV 1080p'
+      before_install:
+        export UUID=$(instruments -s | ruby -e "ARGF.each_line{ |ln| ln =~ /$NAME .* \[(.*)\]/; if \$1; puts(\$1); exit; end }");
+      before_script:
+        carthage bootstrap --platform $PLATFORM --configuration Debug
+      script:
+        - set -o pipefail;
+          open -a "simulator" --args -CurrentDeviceUDID "$UUID"
+          xcodebuild -scheme Dwifft -destination "id=$UUID" test | xcpretty;
 env:
   global:
     secure: pUV8Ccwq1FWO8PxTHPQ3qZDUDhjNOisNVRyrziYR8x0ZQYjmVmnaLKaOHFP8co6rhBlFqReKUjoFYgRqP5QK1EkD6lqSZfnkrWqnZuLDNy0sn3/+4sLtvqAhBroAfjmmvb5GpY+Kkqfz8Lhdu6+1Z/a5xPhqNeDO3aDpa2vdBztb2qLIZnf2g8NFLOoNuR+ula868nIm858LCN1sqLp9PhV4CGaMsFx7ZFaX1yEQbwNb8+85+U2HIHqjZ62u9KZ4lA1d58VVDGj2f7ObKkC+pjiuknljy5bvRVvg5pttXggJracFgMqouwwQFFCV3nFYSDS6h7ZIjUvbyMrqBN+u32RmtqVLp1by1JvRXSeemJ3HxtZGLbq7rff4zWXyYbelT6sR6J1tWIubRo5v3sXc8E1kurqKkcPqJdG4jqiUXOau2oSHhf7WCRwa0KNfvaQuMhm0Onnsi9tW2MzGieumscfuFIJsXJdXnac7jQUVb571GfxMrDeJ9v2GOPcbnlM8cttBFAw4IoINV6teKITUW6T8RpeDXzfQyxDDQaV0M1ZTab1Tj/f4EYDAXKfZ+QquWK3bgXJhxKghXIskZLdDhYMyP55T6QxLZY9wD2CxLrEbEhDQCEb+7R2LmIC95Rlku+W92b8eWAqNxf+vV7QRqBb/sV9w2mlxmmezYTP5VAs=
diff --git a/Dwifft/Dwifft+UIKit.swift b/Dwifft/Dwifft+UIKit.swift
index 3498ec3..8e29769 100644
--- a/Dwifft/Dwifft+UIKit.swift
+++ b/Dwifft/Dwifft+UIKit.swift
@@ -6,7 +6,7 @@
 //  Copyright (c) 2015 jflinter. All rights reserved.
 //
 
-#if os(iOS)
+#if os(iOS) || os(tvOS)
 
 import UIKit
 

From 6d79ad226bcb30bfd71c379f73dc9513267bd834 Mon Sep 17 00:00:00 2001
From: Jan Gorman <gorman.jan@gmail.com>
Date: Thu, 22 Jun 2017 09:02:15 +0200
Subject: [PATCH 09/48] Add tvOS target

---
 Dwifft-tvOS/Dwifft_tvOS.h                     |  18 +++
 Dwifft-tvOS/Info.plist                        |  24 +++
 Dwifft.xcodeproj/project.pbxproj              | 146 ++++++++++++++++++
 .../xcschemes/Dwifft-tvOS.xcscheme            |  80 ++++++++++
 4 files changed, 268 insertions(+)
 create mode 100644 Dwifft-tvOS/Dwifft_tvOS.h
 create mode 100644 Dwifft-tvOS/Info.plist
 create mode 100644 Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-tvOS.xcscheme

diff --git a/Dwifft-tvOS/Dwifft_tvOS.h b/Dwifft-tvOS/Dwifft_tvOS.h
new file mode 100644
index 0000000..026736d
--- /dev/null
+++ b/Dwifft-tvOS/Dwifft_tvOS.h
@@ -0,0 +1,18 @@
+//
+//  Dwifft_tvOS.h
+//  Dwifft-tvOS
+//
+//  Copyright © 2017 jflinter. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+//! Project version number for Dwifft_tvOS.
+FOUNDATION_EXPORT double Dwifft_tvOSVersionNumber;
+
+//! Project version string for Dwifft_tvOS.
+FOUNDATION_EXPORT const unsigned char Dwifft_tvOSVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import <Dwifft_tvOS/PublicHeader.h>
+
+
diff --git a/Dwifft-tvOS/Info.plist b/Dwifft-tvOS/Info.plist
new file mode 100644
index 0000000..1007fd9
--- /dev/null
+++ b/Dwifft-tvOS/Info.plist
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>FMWK</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>$(CURRENT_PROJECT_VERSION)</string>
+	<key>NSPrincipalClass</key>
+	<string></string>
+</dict>
+</plist>
diff --git a/Dwifft.xcodeproj/project.pbxproj b/Dwifft.xcodeproj/project.pbxproj
index 0fdec96..ce6961d 100644
--- a/Dwifft.xcodeproj/project.pbxproj
+++ b/Dwifft.xcodeproj/project.pbxproj
@@ -17,6 +17,10 @@
 		0486A2651EA0A5B600D8093E /* SectionedValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0486A2641EA0A5B600D8093E /* SectionedValues.swift */; };
 		0486A2661EA0A64900D8093E /* SectionedValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0486A2641EA0A5B600D8093E /* SectionedValues.swift */; };
 		04AC329F1E88AEB000EF63DD /* SwiftCheck.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04AC329E1E88AEB000EF63DD /* SwiftCheck.framework */; };
+		94783CB11EFBA21900841579 /* Dwifft_tvOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 94783CAF1EFBA21900841579 /* Dwifft_tvOS.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		94783CB51EFBA25300841579 /* Dwifft.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041EBBAE1B8967C300E113B3 /* Dwifft.swift */; };
+		94783CB61EFBA25300841579 /* SectionedValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0486A2641EA0A5B600D8093E /* SectionedValues.swift */; };
+		94783CB71EFBA25300841579 /* Dwifft+UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041EBBAF1B8967C300E113B3 /* Dwifft+UIKit.swift */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -40,6 +44,9 @@
 		041EBBAF1B8967C300E113B3 /* Dwifft+UIKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Dwifft+UIKit.swift"; sourceTree = "<group>"; };
 		0486A2641EA0A5B600D8093E /* SectionedValues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionedValues.swift; sourceTree = "<group>"; };
 		04AC329E1E88AEB000EF63DD /* SwiftCheck.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftCheck.framework; path = Carthage/Build/iOS/SwiftCheck.framework; sourceTree = SOURCE_ROOT; };
+		94783CAD1EFBA21900841579 /* Dwifft_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Dwifft_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		94783CAF1EFBA21900841579 /* Dwifft_tvOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Dwifft_tvOS.h; sourceTree = "<group>"; };
+		94783CB01EFBA21900841579 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -59,6 +66,13 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		94783CA91EFBA21900841579 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
@@ -67,6 +81,7 @@
 			children = (
 				041EBB941B89679200E113B3 /* Dwifft */,
 				041EBBA11B89679200E113B3 /* DwifftTests */,
+				94783CAE1EFBA21900841579 /* Dwifft-tvOS */,
 				041EBB931B89679200E113B3 /* Products */,
 			);
 			sourceTree = "<group>";
@@ -76,6 +91,7 @@
 			children = (
 				041EBB921B89679200E113B3 /* Dwifft.framework */,
 				041EBB9D1B89679200E113B3 /* DwifftTests.xctest */,
+				94783CAD1EFBA21900841579 /* Dwifft_tvOS.framework */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -118,6 +134,15 @@
 			name = "Supporting Files";
 			sourceTree = "<group>";
 		};
+		94783CAE1EFBA21900841579 /* Dwifft-tvOS */ = {
+			isa = PBXGroup;
+			children = (
+				94783CAF1EFBA21900841579 /* Dwifft_tvOS.h */,
+				94783CB01EFBA21900841579 /* Info.plist */,
+			);
+			path = "Dwifft-tvOS";
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 
 /* Begin PBXHeadersBuildPhase section */
@@ -129,6 +154,14 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		94783CAA1EFBA21900841579 /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				94783CB11EFBA21900841579 /* Dwifft_tvOS.h in Headers */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXHeadersBuildPhase section */
 
 /* Begin PBXNativeTarget section */
@@ -169,6 +202,24 @@
 			productReference = 041EBB9D1B89679200E113B3 /* DwifftTests.xctest */;
 			productType = "com.apple.product-type.bundle.unit-test";
 		};
+		94783CAC1EFBA21900841579 /* Dwifft-tvOS */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 94783CB41EFBA21900841579 /* Build configuration list for PBXNativeTarget "Dwifft-tvOS" */;
+			buildPhases = (
+				94783CA81EFBA21900841579 /* Sources */,
+				94783CA91EFBA21900841579 /* Frameworks */,
+				94783CAA1EFBA21900841579 /* Headers */,
+				94783CAB1EFBA21900841579 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = "Dwifft-tvOS";
+			productName = "Dwifft-tvOS";
+			productReference = 94783CAD1EFBA21900841579 /* Dwifft_tvOS.framework */;
+			productType = "com.apple.product-type.framework";
+		};
 /* End PBXNativeTarget section */
 
 /* Begin PBXProject section */
@@ -188,6 +239,9 @@
 						CreatedOnToolsVersion = 6.4;
 						LastSwiftMigration = 0800;
 					};
+					94783CAC1EFBA21900841579 = {
+						CreatedOnToolsVersion = 9.0;
+					};
 				};
 			};
 			buildConfigurationList = 041EBB8C1B89679200E113B3 /* Build configuration list for PBXProject "Dwifft" */;
@@ -204,6 +258,7 @@
 			targets = (
 				041EBB911B89679200E113B3 /* Dwifft */,
 				041EBB9C1B89679200E113B3 /* DwifftTests */,
+				94783CAC1EFBA21900841579 /* Dwifft-tvOS */,
 			);
 		};
 /* End PBXProject section */
@@ -223,6 +278,13 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		94783CAB1EFBA21900841579 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXShellScriptBuildPhase section */
@@ -264,6 +326,16 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		94783CA81EFBA21900841579 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				94783CB71EFBA25300841579 /* Dwifft+UIKit.swift in Sources */,
+				94783CB61EFBA25300841579 /* SectionedValues.swift in Sources */,
+				94783CB51EFBA25300841579 /* Dwifft.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXTargetDependency section */
@@ -443,6 +515,71 @@
 			};
 			name = Release;
 		};
+		94783CB21EFBA21900841579 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_IDENTITY = "";
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				INFOPLIST_FILE = "Dwifft-tvOS/Info.plist";
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.Dwifft-tvOS";
+				PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+				SDKROOT = appletvos;
+				SKIP_INSTALL = YES;
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+				SWIFT_VERSION = 3.0;
+				TARGETED_DEVICE_FAMILY = 3;
+				TVOS_DEPLOYMENT_TARGET = 9.0;
+			};
+			name = Debug;
+		};
+		94783CB31EFBA21900841579 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_IDENTITY = "";
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				INFOPLIST_FILE = "Dwifft-tvOS/Info.plist";
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.Dwifft-tvOS";
+				PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+				SDKROOT = appletvos;
+				SKIP_INSTALL = YES;
+				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+				SWIFT_VERSION = 3.0;
+				TARGETED_DEVICE_FAMILY = 3;
+				TVOS_DEPLOYMENT_TARGET = 9.0;
+			};
+			name = Release;
+		};
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
@@ -473,6 +610,15 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
+		94783CB41EFBA21900841579 /* Build configuration list for PBXNativeTarget "Dwifft-tvOS" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				94783CB21EFBA21900841579 /* Debug */,
+				94783CB31EFBA21900841579 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 /* End XCConfigurationList section */
 	};
 	rootObject = 041EBB891B89679200E113B3 /* Project object */;
diff --git a/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-tvOS.xcscheme b/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-tvOS.xcscheme
new file mode 100644
index 0000000..7468b85
--- /dev/null
+++ b/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-tvOS.xcscheme
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0900"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "94783CAC1EFBA21900841579"
+               BuildableName = "Dwifft_tvOS.framework"
+               BlueprintName = "Dwifft-tvOS"
+               ReferencedContainer = "container:Dwifft.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "94783CAC1EFBA21900841579"
+            BuildableName = "Dwifft_tvOS.framework"
+            BlueprintName = "Dwifft-tvOS"
+            ReferencedContainer = "container:Dwifft.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "94783CAC1EFBA21900841579"
+            BuildableName = "Dwifft_tvOS.framework"
+            BlueprintName = "Dwifft-tvOS"
+            ReferencedContainer = "container:Dwifft.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

From aaa28dd45562b8bf4cfdfc76d963527136c90c3e Mon Sep 17 00:00:00 2001
From: Jack Flintermann <jflinter11@gmail.com>
Date: Fri, 23 Jun 2017 13:24:02 -0400
Subject: [PATCH 10/48] 0.6.3

---
 Dwifft.podspec | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Dwifft.podspec b/Dwifft.podspec
index 612c9cb..b97fc43 100644
--- a/Dwifft.podspec
+++ b/Dwifft.podspec
@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name = 'Dwifft'
-  s.version = '0.6.2'
+  s.version = '0.6.3'
   s.license = 'MIT'
   s.summary = 'Swift Diff'
   s.homepage = 'https://github.com/jflinter/Dwifft'

From 7f18ec1d0c3783ebc9341c6043e9b87191927471 Mon Sep 17 00:00:00 2001
From: Jesse Squires <jessesquires@users.noreply.github.com>
Date: Thu, 27 Jul 2017 10:24:09 -0700
Subject: [PATCH 11/48] Make `Dwifft` an enum, not a struct. Close #67

---
 Dwifft/Dwifft.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Dwifft/Dwifft.swift b/Dwifft/Dwifft.swift
index 865a989..fbccb64 100644
--- a/Dwifft/Dwifft.swift
+++ b/Dwifft/Dwifft.swift
@@ -76,7 +76,7 @@ public enum SectionedDiffStep<Section, Value>: CustomDebugStringConvertible {
 }
 
 /// Namespace for the `diff` and `apply` functions.
-public struct Dwifft {
+public enum Dwifft {
 
     /// Returns the sequence of `DiffStep`s required to transform one array into another.
     ///

From 3ce020739d7b895bf0a5ce79162ebf1a0f6df399 Mon Sep 17 00:00:00 2001
From: Bryan Irace <bryan@irace.me>
Date: Thu, 10 Aug 2017 10:37:30 -0400
Subject: [PATCH 12/48] Swift 4

---
 Dwifft.xcodeproj/project.pbxproj              | 14 ++++++++-
 .../xcschemes/Dwifft-tvOS.xcscheme            |  2 ++
 .../xcshareddata/xcschemes/Dwifft.xcscheme    |  4 ++-
 Dwifft/Dwifft.swift                           |  2 +-
 .../DwifftExample.xcodeproj/project.pbxproj   | 29 ++++++++++++++++++-
 5 files changed, 47 insertions(+), 4 deletions(-)

diff --git a/Dwifft.xcodeproj/project.pbxproj b/Dwifft.xcodeproj/project.pbxproj
index ce6961d..7336cd0 100644
--- a/Dwifft.xcodeproj/project.pbxproj
+++ b/Dwifft.xcodeproj/project.pbxproj
@@ -228,7 +228,7 @@
 			attributes = {
 				LastSwiftMigration = 0700;
 				LastSwiftUpdateCheck = 0700;
-				LastUpgradeCheck = 0810;
+				LastUpgradeCheck = 0900;
 				ORGANIZATIONNAME = jflinter;
 				TargetAttributes = {
 					041EBB911B89679200E113B3 = {
@@ -355,14 +355,20 @@
 				CLANG_CXX_LIBRARY = "libc++";
 				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
 				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
 				CLANG_WARN_CONSTANT_CONVERSION = YES;
 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
 				CLANG_WARN_EMPTY_BODY = YES;
 				CLANG_WARN_ENUM_CONVERSION = YES;
 				CLANG_WARN_INFINITE_RECURSION = YES;
 				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
 				CLANG_WARN_SUSPICIOUS_MOVE = YES;
 				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@@ -407,14 +413,20 @@
 				CLANG_CXX_LIBRARY = "libc++";
 				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
 				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
 				CLANG_WARN_CONSTANT_CONVERSION = YES;
 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
 				CLANG_WARN_EMPTY_BODY = YES;
 				CLANG_WARN_ENUM_CONVERSION = YES;
 				CLANG_WARN_INFINITE_RECURSION = YES;
 				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
 				CLANG_WARN_SUSPICIOUS_MOVE = YES;
 				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
diff --git a/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-tvOS.xcscheme b/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-tvOS.xcscheme
index 7468b85..2a096fe 100644
--- a/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-tvOS.xcscheme
+++ b/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-tvOS.xcscheme
@@ -26,6 +26,7 @@
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      language = ""
       shouldUseLaunchSchemeArgsEnv = "YES">
       <Testables>
       </Testables>
@@ -36,6 +37,7 @@
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      language = ""
       launchStyle = "0"
       useCustomWorkingDirectory = "NO"
       ignoresPersistentStateOnLaunch = "NO"
diff --git a/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft.xcscheme b/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft.xcscheme
index d35c757..28b227b 100644
--- a/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft.xcscheme
+++ b/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "0810"
+   LastUpgradeVersion = "0900"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"
@@ -40,6 +40,7 @@
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      language = ""
       shouldUseLaunchSchemeArgsEnv = "YES"
       codeCoverageEnabled = "YES">
       <Testables>
@@ -70,6 +71,7 @@
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      language = ""
       launchStyle = "0"
       useCustomWorkingDirectory = "NO"
       ignoresPersistentStateOnLaunch = "NO"
diff --git a/Dwifft/Dwifft.swift b/Dwifft/Dwifft.swift
index fbccb64..4ed7a4a 100644
--- a/Dwifft/Dwifft.swift
+++ b/Dwifft/Dwifft.swift
@@ -128,7 +128,7 @@ public enum Dwifft {
     ///   - lhs: a `SectionedValues`
     ///   - rhs: another, uh, `SectionedValues`
     /// - Returns: the series of transformations that, when applied to `lhs`, will yield `rhs`.
-    public static func diff<Section: Equatable, Value: Equatable>(lhs: SectionedValues<Section, Value>, rhs: SectionedValues<Section, Value>) -> [SectionedDiffStep<Section, Value>] {
+    public static func diff<Section, Value>(lhs: SectionedValues<Section, Value>, rhs: SectionedValues<Section, Value>) -> [SectionedDiffStep<Section, Value>] {
         if lhs.sections == rhs.sections {
             let allResults: [[SectionedDiffStep<Section, Value>]] = (0..<lhs.sections.count).map { i in
                 let lValues = lhs.sectionsAndValues[i].1
diff --git a/DwifftExample/DwifftExample.xcodeproj/project.pbxproj b/DwifftExample/DwifftExample.xcodeproj/project.pbxproj
index 66b3bec..b96656e 100644
--- a/DwifftExample/DwifftExample.xcodeproj/project.pbxproj
+++ b/DwifftExample/DwifftExample.xcodeproj/project.pbxproj
@@ -48,6 +48,13 @@
 			remoteGlobalIDString = 041EBB911B89679200E113B3;
 			remoteInfo = Dwifft;
 		};
+		931446A91F3CA6FB00471406 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 045502181B898C5400F5614D /* Dwifft.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 94783CAD1EFBA21900841579;
+			remoteInfo = "Dwifft-tvOS";
+		};
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXCopyFilesBuildPhase section */
@@ -136,6 +143,7 @@
 			children = (
 				0455021E1B898C5400F5614D /* Dwifft.framework */,
 				045502201B898C5400F5614D /* DwifftTests.xctest */,
+				931446AA1F3CA6FB00471406 /* Dwifft_tvOS.framework */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -170,7 +178,7 @@
 			isa = PBXProject;
 			attributes = {
 				LastSwiftUpdateCheck = 0700;
-				LastUpgradeCheck = 0800;
+				LastUpgradeCheck = 0900;
 				ORGANIZATIONNAME = jflinter;
 				TargetAttributes = {
 					045501F21B898C3200F5614D = {
@@ -218,6 +226,13 @@
 			remoteRef = 0455021F1B898C5400F5614D /* PBXContainerItemProxy */;
 			sourceTree = BUILT_PRODUCTS_DIR;
 		};
+		931446AA1F3CA6FB00471406 /* Dwifft_tvOS.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = Dwifft_tvOS.framework;
+			remoteRef = 931446A91F3CA6FB00471406 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
 /* End PBXReferenceProxy section */
 
 /* Begin PBXResourcesBuildPhase section */
@@ -289,14 +304,20 @@
 				CLANG_CXX_LIBRARY = "libc++";
 				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
 				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
 				CLANG_WARN_CONSTANT_CONVERSION = YES;
 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
 				CLANG_WARN_EMPTY_BODY = YES;
 				CLANG_WARN_ENUM_CONVERSION = YES;
 				CLANG_WARN_INFINITE_RECURSION = YES;
 				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
 				CLANG_WARN_SUSPICIOUS_MOVE = YES;
 				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@@ -336,14 +357,20 @@
 				CLANG_CXX_LIBRARY = "libc++";
 				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
 				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
 				CLANG_WARN_CONSTANT_CONVERSION = YES;
 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
 				CLANG_WARN_EMPTY_BODY = YES;
 				CLANG_WARN_ENUM_CONVERSION = YES;
 				CLANG_WARN_INFINITE_RECURSION = YES;
 				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
 				CLANG_WARN_SUSPICIOUS_MOVE = YES;
 				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;

From 3a7b79b7f753fc6ad109767c7448d865904b4ad3 Mon Sep 17 00:00:00 2001
From: Rik Chilvers <rikchilvers@fastmail.com>
Date: Sun, 27 Aug 2017 08:24:14 +0100
Subject: [PATCH 13/48] initial macOS support

Needs test coverage.
---
 Dwifft-macOS/Dwifft_macOS.h                   |  18 ++
 Dwifft-macOS/Info.plist                       |  26 ++
 Dwifft.xcodeproj/project.pbxproj              | 146 ++++++++++
 .../xcschemes/Dwifft-macOS.xcscheme           | 102 +++++++
 Dwifft/Dwifft+AppKit.swift                    | 266 ++++++++++++++++++
 5 files changed, 558 insertions(+)
 create mode 100644 Dwifft-macOS/Dwifft_macOS.h
 create mode 100644 Dwifft-macOS/Info.plist
 create mode 100644 Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-macOS.xcscheme
 create mode 100644 Dwifft/Dwifft+AppKit.swift

diff --git a/Dwifft-macOS/Dwifft_macOS.h b/Dwifft-macOS/Dwifft_macOS.h
new file mode 100644
index 0000000..3bf8a9c
--- /dev/null
+++ b/Dwifft-macOS/Dwifft_macOS.h
@@ -0,0 +1,18 @@
+//
+//  Dwifft_macOS.h
+//  Dwifft-macOS
+//
+//  Copyright © 2017 jflinter. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+//! Project version number for Dwifft_macOS.
+FOUNDATION_EXPORT double Dwifft_macOSVersionNumber;
+
+//! Project version string for Dwifft_macOS.
+FOUNDATION_EXPORT const unsigned char Dwifft_macOSVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import <Dwifft_macOS/PublicHeader.h>
+
+
diff --git a/Dwifft-macOS/Info.plist b/Dwifft-macOS/Info.plist
new file mode 100644
index 0000000..58e6a64
--- /dev/null
+++ b/Dwifft-macOS/Info.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>FMWK</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>$(CURRENT_PROJECT_VERSION)</string>
+	<key>NSHumanReadableCopyright</key>
+	<string>Copyright © 2017 jflinter. All rights reserved.</string>
+	<key>NSPrincipalClass</key>
+	<string></string>
+</dict>
+</plist>
diff --git a/Dwifft.xcodeproj/project.pbxproj b/Dwifft.xcodeproj/project.pbxproj
index 7336cd0..eefb9f0 100644
--- a/Dwifft.xcodeproj/project.pbxproj
+++ b/Dwifft.xcodeproj/project.pbxproj
@@ -17,6 +17,10 @@
 		0486A2651EA0A5B600D8093E /* SectionedValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0486A2641EA0A5B600D8093E /* SectionedValues.swift */; };
 		0486A2661EA0A64900D8093E /* SectionedValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0486A2641EA0A5B600D8093E /* SectionedValues.swift */; };
 		04AC329F1E88AEB000EF63DD /* SwiftCheck.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04AC329E1E88AEB000EF63DD /* SwiftCheck.framework */; };
+		382DEF461F487396007A8FD2 /* Dwifft_macOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 382DEF441F487395007A8FD2 /* Dwifft_macOS.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		382DEF4B1F487519007A8FD2 /* Dwifft.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041EBBAE1B8967C300E113B3 /* Dwifft.swift */; };
+		382DEF4C1F48751B007A8FD2 /* SectionedValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0486A2641EA0A5B600D8093E /* SectionedValues.swift */; };
+		382DEF4D1F48751C007A8FD2 /* Dwifft+AppKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 382DEF4A1F4873F9007A8FD2 /* Dwifft+AppKit.swift */; };
 		94783CB11EFBA21900841579 /* Dwifft_tvOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 94783CAF1EFBA21900841579 /* Dwifft_tvOS.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		94783CB51EFBA25300841579 /* Dwifft.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041EBBAE1B8967C300E113B3 /* Dwifft.swift */; };
 		94783CB61EFBA25300841579 /* SectionedValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0486A2641EA0A5B600D8093E /* SectionedValues.swift */; };
@@ -44,6 +48,10 @@
 		041EBBAF1B8967C300E113B3 /* Dwifft+UIKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Dwifft+UIKit.swift"; sourceTree = "<group>"; };
 		0486A2641EA0A5B600D8093E /* SectionedValues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionedValues.swift; sourceTree = "<group>"; };
 		04AC329E1E88AEB000EF63DD /* SwiftCheck.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftCheck.framework; path = Carthage/Build/iOS/SwiftCheck.framework; sourceTree = SOURCE_ROOT; };
+		382DEF421F487395007A8FD2 /* Dwifft_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Dwifft_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		382DEF441F487395007A8FD2 /* Dwifft_macOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Dwifft_macOS.h; sourceTree = "<group>"; };
+		382DEF451F487395007A8FD2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		382DEF4A1F4873F9007A8FD2 /* Dwifft+AppKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dwifft+AppKit.swift"; sourceTree = "<group>"; };
 		94783CAD1EFBA21900841579 /* Dwifft_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Dwifft_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		94783CAF1EFBA21900841579 /* Dwifft_tvOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Dwifft_tvOS.h; sourceTree = "<group>"; };
 		94783CB01EFBA21900841579 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -66,6 +74,13 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		382DEF3E1F487395007A8FD2 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		94783CA91EFBA21900841579 /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
@@ -82,6 +97,7 @@
 				041EBB941B89679200E113B3 /* Dwifft */,
 				041EBBA11B89679200E113B3 /* DwifftTests */,
 				94783CAE1EFBA21900841579 /* Dwifft-tvOS */,
+				382DEF431F487395007A8FD2 /* Dwifft-macOS */,
 				041EBB931B89679200E113B3 /* Products */,
 			);
 			sourceTree = "<group>";
@@ -92,6 +108,7 @@
 				041EBB921B89679200E113B3 /* Dwifft.framework */,
 				041EBB9D1B89679200E113B3 /* DwifftTests.xctest */,
 				94783CAD1EFBA21900841579 /* Dwifft_tvOS.framework */,
+				382DEF421F487395007A8FD2 /* Dwifft_macOS.framework */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -103,6 +120,7 @@
 				041EBBAE1B8967C300E113B3 /* Dwifft.swift */,
 				0486A2641EA0A5B600D8093E /* SectionedValues.swift */,
 				041EBBAF1B8967C300E113B3 /* Dwifft+UIKit.swift */,
+				382DEF4A1F4873F9007A8FD2 /* Dwifft+AppKit.swift */,
 				041EBB951B89679200E113B3 /* Supporting Files */,
 			);
 			path = Dwifft;
@@ -134,6 +152,15 @@
 			name = "Supporting Files";
 			sourceTree = "<group>";
 		};
+		382DEF431F487395007A8FD2 /* Dwifft-macOS */ = {
+			isa = PBXGroup;
+			children = (
+				382DEF441F487395007A8FD2 /* Dwifft_macOS.h */,
+				382DEF451F487395007A8FD2 /* Info.plist */,
+			);
+			path = "Dwifft-macOS";
+			sourceTree = "<group>";
+		};
 		94783CAE1EFBA21900841579 /* Dwifft-tvOS */ = {
 			isa = PBXGroup;
 			children = (
@@ -154,6 +181,14 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		382DEF3F1F487395007A8FD2 /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				382DEF461F487396007A8FD2 /* Dwifft_macOS.h in Headers */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		94783CAA1EFBA21900841579 /* Headers */ = {
 			isa = PBXHeadersBuildPhase;
 			buildActionMask = 2147483647;
@@ -202,6 +237,24 @@
 			productReference = 041EBB9D1B89679200E113B3 /* DwifftTests.xctest */;
 			productType = "com.apple.product-type.bundle.unit-test";
 		};
+		382DEF411F487395007A8FD2 /* Dwifft-macOS */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 382DEF491F487396007A8FD2 /* Build configuration list for PBXNativeTarget "Dwifft-macOS" */;
+			buildPhases = (
+				382DEF3D1F487395007A8FD2 /* Sources */,
+				382DEF3E1F487395007A8FD2 /* Frameworks */,
+				382DEF3F1F487395007A8FD2 /* Headers */,
+				382DEF401F487395007A8FD2 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = "Dwifft-macOS";
+			productName = "Dwifft-macOS";
+			productReference = 382DEF421F487395007A8FD2 /* Dwifft_macOS.framework */;
+			productType = "com.apple.product-type.framework";
+		};
 		94783CAC1EFBA21900841579 /* Dwifft-tvOS */ = {
 			isa = PBXNativeTarget;
 			buildConfigurationList = 94783CB41EFBA21900841579 /* Build configuration list for PBXNativeTarget "Dwifft-tvOS" */;
@@ -239,6 +292,10 @@
 						CreatedOnToolsVersion = 6.4;
 						LastSwiftMigration = 0800;
 					};
+					382DEF411F487395007A8FD2 = {
+						CreatedOnToolsVersion = 9.0;
+						LastSwiftMigration = 0900;
+					};
 					94783CAC1EFBA21900841579 = {
 						CreatedOnToolsVersion = 9.0;
 					};
@@ -259,6 +316,7 @@
 				041EBB911B89679200E113B3 /* Dwifft */,
 				041EBB9C1B89679200E113B3 /* DwifftTests */,
 				94783CAC1EFBA21900841579 /* Dwifft-tvOS */,
+				382DEF411F487395007A8FD2 /* Dwifft-macOS */,
 			);
 		};
 /* End PBXProject section */
@@ -278,6 +336,13 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		382DEF401F487395007A8FD2 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		94783CAB1EFBA21900841579 /* Resources */ = {
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -326,6 +391,16 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		382DEF3D1F487395007A8FD2 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				382DEF4B1F487519007A8FD2 /* Dwifft.swift in Sources */,
+				382DEF4C1F48751B007A8FD2 /* SectionedValues.swift in Sources */,
+				382DEF4D1F48751C007A8FD2 /* Dwifft+AppKit.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		94783CA81EFBA21900841579 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -527,6 +602,68 @@
 			};
 			name = Release;
 		};
+		382DEF471F487396007A8FD2 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_IDENTITY = "-";
+				COMBINE_HIDPI_IMAGES = YES;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				FRAMEWORK_VERSION = A;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				INFOPLIST_FILE = "Dwifft-macOS/Info.plist";
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.12;
+				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.Dwifft-macOS";
+				PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+				SDKROOT = macosx;
+				SKIP_INSTALL = YES;
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_VERSION = 4.0;
+			};
+			name = Debug;
+		};
+		382DEF481F487396007A8FD2 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_IDENTITY = "-";
+				COMBINE_HIDPI_IMAGES = YES;
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				FRAMEWORK_VERSION = A;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				INFOPLIST_FILE = "Dwifft-macOS/Info.plist";
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.12;
+				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.Dwifft-macOS";
+				PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+				SDKROOT = macosx;
+				SKIP_INSTALL = YES;
+				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+				SWIFT_VERSION = 4.0;
+			};
+			name = Release;
+		};
 		94783CB21EFBA21900841579 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
@@ -622,6 +759,15 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
+		382DEF491F487396007A8FD2 /* Build configuration list for PBXNativeTarget "Dwifft-macOS" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				382DEF471F487396007A8FD2 /* Debug */,
+				382DEF481F487396007A8FD2 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 		94783CB41EFBA21900841579 /* Build configuration list for PBXNativeTarget "Dwifft-tvOS" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
diff --git a/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-macOS.xcscheme b/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-macOS.xcscheme
new file mode 100644
index 0000000..c31dfbf
--- /dev/null
+++ b/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-macOS.xcscheme
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0900"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "382DEF411F487395007A8FD2"
+               BuildableName = "Dwifft_macOS.framework"
+               BlueprintName = "Dwifft-macOS"
+               ReferencedContainer = "container:Dwifft.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      language = ""
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      codeCoverageEnabled = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "041EBB9C1B89679200E113B3"
+               BuildableName = "DwifftTests.xctest"
+               BlueprintName = "DwifftTests"
+               ReferencedContainer = "container:Dwifft.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "382DEF411F487395007A8FD2"
+            BuildableName = "Dwifft_macOS.framework"
+            BlueprintName = "Dwifft-macOS"
+            ReferencedContainer = "container:Dwifft.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      language = ""
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "382DEF411F487395007A8FD2"
+            BuildableName = "Dwifft_macOS.framework"
+            BlueprintName = "Dwifft-macOS"
+            ReferencedContainer = "container:Dwifft.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "382DEF411F487395007A8FD2"
+            BuildableName = "Dwifft_macOS.framework"
+            BlueprintName = "Dwifft-macOS"
+            ReferencedContainer = "container:Dwifft.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/Dwifft/Dwifft+AppKit.swift b/Dwifft/Dwifft+AppKit.swift
new file mode 100644
index 0000000..7644108
--- /dev/null
+++ b/Dwifft/Dwifft+AppKit.swift
@@ -0,0 +1,266 @@
+//
+//  Dwifft+NSKit.swift
+//  Dwifft
+//
+//  Created by Jack Flintermann on 3/13/15.
+//  Copyright (c) 2015 jflinter. All rights reserved.
+//
+
+#if os(OSX)
+
+import Cocoa
+
+
+/// A parent class for all diff calculators. Don't use it directly.
+public class AbstractDiffCalculator<Section: Equatable, Value: Equatable> {
+
+    fileprivate init(initialSectionedValues: SectionedValues<Section, Value>) {
+        self._sectionedValues = initialSectionedValues
+    }
+
+    /// The number of sections in the diff calculator. Return this inside
+    /// `numberOfSections(in: tableView)` or `numberOfSections(in: collectionView)`.
+    /// Don't implement that method any other way (see the docs for `numberOfObjects(inSection:)`
+    /// for more context).
+    public final func numberOfSections() -> Int {
+        return self.sectionedValues.sections.count
+    }
+
+    /// The section at a given index. If you implement `tableView:titleForHeaderInSection` or
+    /// `collectionView:viewForSupplementaryElementOfKind:atIndexPath`, you can use this
+    /// method to get information about that section out of Dwifft.
+    ///
+    /// - Parameter forSection: the index of the section you care about.
+    /// - Returns: the Section at that index.
+    public final func value(forSection: Int) -> Section {
+        return self.sectionedValues[forSection].0
+    }
+
+
+    /// The, uh, number of objects in a given section. Use this to implement
+    /// `NSTableViewDataSource.numberOfRowsInSection:` or `NSCollectionViewDataSource.numberOfItemsInSection:`.
+    /// Seriously, don't implement that method any other way - there is some subtle timing stuff
+    /// around when this value should change in order to satisfy `NSTableView`/`NSCollectionView`'s internal
+    /// assertions, that Dwifft knows how to handle correctly. Read the source for
+    /// Dwifft+NSKit.swift if you don't believe me/want to learn more.
+    ///
+    /// - Parameter section: a section of your table/collection view
+    /// - Returns: the number of objects in that section.
+    public final func numberOfObjects(inSection section: Int) -> Int {
+        return self.sectionedValues[section].1.count
+    }
+
+
+    /// The value at a given index path. Use this to implement
+    /// `NSTableViewDataSource.objectValueForRow` or `NSCollectionViewDataSource.itemForRepresentedObjectAtIndexPath`.
+    ///
+    /// - Parameter indexPath: the index path you are interested in
+    /// - Returns: the thing at that index path
+    public final func value(atIndexPath indexPath: IndexPath) -> Value {
+        return self.sectionedValues[indexPath.section].1[indexPath.item]
+    }
+
+
+    /// Set this variable to automatically trigger the correct section/row/item insertion/deletions
+    /// on your table/collection view.
+    public final var sectionedValues: SectionedValues<Section, Value> {
+        get {
+            return _sectionedValues
+        }
+        set {
+            let oldSectionedValues = sectionedValues
+            let newSectionedValues = newValue
+            let diff = Dwifft.diff(lhs: oldSectionedValues, rhs: newSectionedValues)
+            if (diff.count > 0) {
+                self.processChanges(newState: newSectionedValues, diff: diff)
+            }
+        }
+    }
+
+    // NSTableView and NSCollectionView both perform assertions on the *current* number of rows/items before performing any updates. As such, the `sectionedValues` property must be backed by an internal value that does not change until *after* `beginUpdates`/`performBatchUpdates` has been called.
+    fileprivate final var _sectionedValues: SectionedValues<Section, Value>
+    fileprivate func processChanges(newState: SectionedValues<Section, Value>, diff: [SectionedDiffStep<Section, Value>]){
+        fatalError("override me")
+    }
+}
+
+/// This class manages a `NSTableView`'s rows and sections. It will make the necessary calls to
+/// the table view to ensure that its UI is kept in sync with the contents of the `sectionedValues` property.
+public final class TableViewDiffCalculator<Section: Equatable, Value: Equatable>: AbstractDiffCalculator<Section, Value> {
+
+    /// The table view to be managed
+    public weak var tableView: NSTableView?
+
+    /// Initializes a new diff calculator.
+    ///
+    /// - Parameters:
+    ///   - tableView: the table view to be managed
+    ///   - initialSectionedValues: optional - if specified, these will be the initial contents of the diff calculator.
+    public init(tableView: NSTableView?, initialSectionedValues: SectionedValues<Section, Value> = SectionedValues()) {
+        self.tableView = tableView
+        super.init(initialSectionedValues: initialSectionedValues)
+    }
+
+    /// You can change insertion/deletion animations like this! Fade works well.
+    /// So does Top/Bottom. Left/Right/Middle are a little weird, but hey, do your thing.
+    public var insertionAnimation = NSTableView.AnimationOptions.slideUp, deletionAnimation = NSTableView.AnimationOptions.slideUp
+
+    override fileprivate func processChanges(newState: SectionedValues<Section, Value>, diff: [SectionedDiffStep<Section, Value>]) {
+        guard let tableView = self.tableView else { return }
+        tableView.beginUpdates()
+        self._sectionedValues = newState
+        for result in diff {
+            switch result {
+            case let .delete(_, row, _): tableView.removeRows(at: [row], withAnimation: self.deletionAnimation)
+            case let .insert(_, row, _): tableView.insertRows(at: [row], withAnimation: self.insertionAnimation)
+            default: fatalError("NSTableViews do not have sections")
+            }
+        }
+        tableView.endUpdates()
+    }
+}
+
+/// This class manages a `NSCollectionView`'s items and sections. It will make the necessary
+/// calls to the collection view to ensure that its UI is kept in sync with the contents
+/// of the `sectionedValues` property.
+public final class CollectionViewDiffCalculator<Section: Equatable, Value: Equatable> : AbstractDiffCalculator<Section, Value> {
+
+    /// The collection view to be managed.
+    public weak var collectionView: NSCollectionView?
+
+    /// Initializes a new diff calculator.
+    ///
+    /// - Parameters:
+    ///   - collectionView: the collection view to be managed.
+    ///   - initialSectionedValues: optional - if specified, these will be the initial contents of the diff calculator.
+    public init(collectionView: NSCollectionView?, initialSectionedValues: SectionedValues<Section, Value> = SectionedValues()) {
+        self.collectionView = collectionView
+        super.init(initialSectionedValues: initialSectionedValues)
+    }
+
+    override fileprivate func processChanges(newState: SectionedValues<Section, Value>, diff: [SectionedDiffStep<Section, Value>]) {
+        guard let collectionView = self.collectionView else { return }
+        collectionView.performBatchUpdates({
+            self._sectionedValues = newState
+            for result in diff {
+                switch result {
+                case let .delete(section, item, _): collectionView.deleteItems(at: [IndexPath(item: item, section: section)])
+                case let .insert(section, item, _): collectionView.insertItems(at: [IndexPath(item: item, section: section)])
+                case let .sectionDelete(section, _): collectionView.deleteSections(IndexSet(integer: section))
+                case let .sectionInsert(section, _): collectionView.insertSections(IndexSet(integer: section))
+                }
+            }
+        }, completionHandler: nil)
+    }
+}
+
+/// Let's say your data model consists of different sections containing different model types. Since
+/// `SectionedValues` requires a uniform type for all of its rows, this can be a clunky situation. You
+/// can address this in a couple of ways. The first is to define a custom enum that encompasses all of the
+/// things that *could* be in your data model - if section 1 has a bunch of `String`s, and section 2 has a bunch
+/// of `Int`s, define a `StringOrInt` enum that conforms to `Equatable`, and fill the `SectionedValues`
+/// that you use to drive your DiffCalculator up with those. Alternatively, if you are lazy, and your
+/// models all conform to `Hashable`, you can use a SimpleTableViewDiffCalculator instead.
+typealias SimpleTableViewDiffCalculator = TableViewDiffCalculator<AnyHashable, AnyHashable>
+
+/// See SimpleTableViewDiffCalculator for explanation
+typealias SimpleCollectionViewDiffCalculator = CollectionViewDiffCalculator<AnyHashable, AnyHashable>
+
+/// If your table view only has a single section, or you only want to power a single section of it with Dwifft,
+/// use a `SingleSectionTableViewDiffCalculator`. Note that this approach is not highly recommended, and you should
+/// do so only if it *really* doesn't make sense to just power your whole table with a `TableViewDiffCalculator`.
+/// You'll be less likely to mess up the index math :P
+public final class SingleSectionTableViewDiffCalculator<Value: Equatable> {
+
+    /// The table view to be managed
+    public weak var tableView: NSTableView?
+
+    /// All insertion/deletion calls will be made on this index.
+    public let sectionIndex: Int
+
+    /// You can change insertion/deletion animations like this! Fade works well.
+    /// So does Top/Bottom. Left/Right/Middle are a little weird, but hey, do your thing.
+    public var insertionAnimation = NSTableView.AnimationOptions.slideUp {
+        didSet {
+            self.internalDiffCalculator.insertionAnimation = self.insertionAnimation 
+        }
+    }
+    
+    public var deletionAnimation = NSTableView.AnimationOptions.slideUp {
+        didSet {
+            self.internalDiffCalculator.deletionAnimation = self.deletionAnimation 
+        }
+    }
+
+    /// Set this variable to automatically trigger the correct row insertion/deletions
+    /// on your table view.
+    public var rows : [Value] {
+        get {
+            return self.internalDiffCalculator.sectionedValues[self.sectionIndex].1
+        }
+        set {
+            self.internalDiffCalculator.sectionedValues = SingleSectionTableViewDiffCalculator.buildSectionedValues(values: newValue, sectionIndex: self.sectionIndex)
+        }
+    }
+
+    /// Initializes a new diff calculator.
+    ///
+    /// - Parameters:
+    ///   - tableView: the table view to be managed
+    ///   - initialRows: optional - if specified, these will be the initial contents of the diff calculator.
+    ///   - sectionIndex: optional - all insertion/deletion calls will be made on this index.
+    public init(tableView: NSTableView?, initialRows: [Value] = [], sectionIndex: Int = 0) {
+        self.tableView = tableView
+        self.internalDiffCalculator = TableViewDiffCalculator(tableView: tableView, initialSectionedValues: SingleSectionTableViewDiffCalculator.buildSectionedValues(values: initialRows, sectionIndex: sectionIndex))
+        self.sectionIndex = sectionIndex
+    }
+
+    fileprivate static func buildSectionedValues(values: [Value], sectionIndex: Int) -> SectionedValues<Int, Value> {
+        let firstRows = (0..<sectionIndex).map { ($0, [Value]()) }
+        return SectionedValues(firstRows + [(sectionIndex, values)])
+    }
+
+    private let internalDiffCalculator: TableViewDiffCalculator<Int, Value>
+
+}
+
+/// If your collection view only has a single section, or you only want to power a single section of it with Dwifft,
+/// use a `SingleSectionCollectionViewDiffCalculator`. Note that this approach is not highly recommended, and you should
+/// do so only if it *really* doesn't make sense to just power your whole view with a `CollectionViewDiffCalculator`.
+/// You'll be less likely to mess up the index math :P
+public final class SingleSectionCollectionViewDiffCalculator<Value: Equatable> {
+
+    /// The collection view to be managed
+    public weak var collectionView: NSCollectionView?
+
+    /// All insertion/deletion calls will be made for items at this section.
+    public let sectionIndex: Int
+
+    /// Set this variable to automatically trigger the correct item insertion/deletions
+    /// on your collection view.
+    public var items : [Value] {
+        get {
+            return self.internalDiffCalculator.sectionedValues[self.sectionIndex].1
+        }
+        set {
+            self.internalDiffCalculator.sectionedValues = SingleSectionTableViewDiffCalculator.buildSectionedValues(values: newValue, sectionIndex: self.sectionIndex)
+        }
+    }
+
+    /// Initializes a new diff calculator.
+    ///
+    /// - Parameters:
+    ///   - tableView: the table view to be managed
+    ///   - initialItems: optional - if specified, these will be the initial contents of the diff calculator.
+    ///   - sectionIndex: optional - all insertion/deletion calls will be made on this index.
+    public init(collectionView: NSCollectionView?, initialItems: [Value] = [], sectionIndex: Int = 0) {
+        self.collectionView = collectionView
+        self.internalDiffCalculator = CollectionViewDiffCalculator(collectionView: collectionView, initialSectionedValues: SingleSectionTableViewDiffCalculator.buildSectionedValues(values: initialItems, sectionIndex: sectionIndex))
+        self.sectionIndex = sectionIndex
+    }
+
+    private let internalDiffCalculator: CollectionViewDiffCalculator<Int, Value>
+    
+}
+
+#endif

From 77b59a872c411ce2e8d529fbcd19dd24b77922e1 Mon Sep 17 00:00:00 2001
From: Jack Flintermann <jack@stripe.com>
Date: Tue, 19 Sep 2017 23:00:46 -0400
Subject: [PATCH 14/48] update travis

---
 .travis.yml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 42a33da..d2b804e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,9 +2,9 @@ matrix:
   include:
     - os: osx
       language: objective-c
-      osx_image: xcode8
+      osx_image: xcode9
       env:
-        - PLATFORM=iOS NAME='iPhone 7'
+        - PLATFORM=iOS NAME='iPhone 8'
       before_install:
         export UUID=$(instruments -s | ruby -e "ARGF.each_line{ |ln| ln =~ /$NAME .* \[(.*)\]/; if \$1; puts(\$1); exit; end }");
       before_script:
@@ -15,7 +15,7 @@ matrix:
           xcodebuild -scheme Dwifft -destination "id=$UUID" test | xcpretty;
     - os: osx
       language: objective-c
-      osx_image: xcode8
+      osx_image: xcode9
       env:
         - PLATFORM=tvOS NAME='Apple TV 1080p'
       before_install:

From afc1cd8b61c860f678df15d408de6b6c0873179d Mon Sep 17 00:00:00 2001
From: Jack Flintermann <jack@stripe.com>
Date: Wed, 20 Sep 2017 00:42:27 -0400
Subject: [PATCH 15/48] migrate tests

---
 Cartfile.private                 |  2 +-
 Cartfile.resolved                |  2 +-
 Dwifft.xcodeproj/project.pbxproj | 16 ++++++++++------
 3 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/Cartfile.private b/Cartfile.private
index f76f5c3..ad6fa14 100644
--- a/Cartfile.private
+++ b/Cartfile.private
@@ -1 +1 @@
-github "typelift/SwiftCheck" "0.7.3"
\ No newline at end of file
+github "jflinter/SwiftCheck" "8cb33343f45bc1b2f621011e096dbdd7e66787d6"
\ No newline at end of file
diff --git a/Cartfile.resolved b/Cartfile.resolved
index 0ee3ec3..1abd40f 100644
--- a/Cartfile.resolved
+++ b/Cartfile.resolved
@@ -1 +1 @@
-github "typelift/SwiftCheck" "0.7.3"
+github "jflinter/SwiftCheck" "8cb33343f45bc1b2f621011e096dbdd7e66787d6"
diff --git a/Dwifft.xcodeproj/project.pbxproj b/Dwifft.xcodeproj/project.pbxproj
index 7336cd0..92c8733 100644
--- a/Dwifft.xcodeproj/project.pbxproj
+++ b/Dwifft.xcodeproj/project.pbxproj
@@ -233,11 +233,11 @@
 				TargetAttributes = {
 					041EBB911B89679200E113B3 = {
 						CreatedOnToolsVersion = 6.4;
-						LastSwiftMigration = 0800;
+						LastSwiftMigration = 0900;
 					};
 					041EBB9C1B89679200E113B3 = {
 						CreatedOnToolsVersion = 6.4;
-						LastSwiftMigration = 0800;
+						LastSwiftMigration = 0900;
 					};
 					94783CAC1EFBA21900841579 = {
 						CreatedOnToolsVersion = 9.0;
@@ -469,7 +469,8 @@
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
-				SWIFT_VERSION = 3.0;
+				SWIFT_SWIFT3_OBJC_INFERENCE = On;
+				SWIFT_VERSION = 4.0;
 			};
 			name = Debug;
 		};
@@ -488,7 +489,8 @@
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
-				SWIFT_VERSION = 3.0;
+				SWIFT_SWIFT3_OBJC_INFERENCE = On;
+				SWIFT_VERSION = 4.0;
 			};
 			name = Release;
 		};
@@ -507,7 +509,8 @@
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SWIFT_VERSION = 3.0;
+				SWIFT_SWIFT3_OBJC_INFERENCE = On;
+				SWIFT_VERSION = 4.0;
 			};
 			name = Debug;
 		};
@@ -523,7 +526,8 @@
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
-				SWIFT_VERSION = 3.0;
+				SWIFT_SWIFT3_OBJC_INFERENCE = On;
+				SWIFT_VERSION = 4.0;
 			};
 			name = Release;
 		};

From 709bf28a0173acb93f70ae22eeaf99f9134d5b8d Mon Sep 17 00:00:00 2001
From: Jack Flintermann <jack@stripe.com>
Date: Wed, 20 Sep 2017 14:20:54 -0400
Subject: [PATCH 16/48] travis

---
 .travis.yml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index d2b804e..f4947d4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -11,8 +11,8 @@ matrix:
         carthage bootstrap --platform $PLATFORM --configuration Debug
       script:
         - set -o pipefail;
-          open -a "simulator" --args -CurrentDeviceUDID "$UUID"
-          xcodebuild -scheme Dwifft -destination "id=$UUID" test | xcpretty;
+        - open -a "simulator" --args -CurrentDeviceUDID "$UUID"
+        - xcodebuild -scheme Dwifft -destination "id=$UUID" test | xcpretty
     - os: osx
       language: objective-c
       osx_image: xcode9
@@ -24,8 +24,8 @@ matrix:
         carthage bootstrap --platform $PLATFORM --configuration Debug
       script:
         - set -o pipefail;
-          open -a "simulator" --args -CurrentDeviceUDID "$UUID"
-          xcodebuild -scheme Dwifft -destination "id=$UUID" test | xcpretty;
+        - open -a "simulator" --args -CurrentDeviceUDID "$UUID"
+        - xcodebuild -scheme Dwifft -destination "id=$UUID" test | xcpretty
 env:
   global:
     secure: pUV8Ccwq1FWO8PxTHPQ3qZDUDhjNOisNVRyrziYR8x0ZQYjmVmnaLKaOHFP8co6rhBlFqReKUjoFYgRqP5QK1EkD6lqSZfnkrWqnZuLDNy0sn3/+4sLtvqAhBroAfjmmvb5GpY+Kkqfz8Lhdu6+1Z/a5xPhqNeDO3aDpa2vdBztb2qLIZnf2g8NFLOoNuR+ula868nIm858LCN1sqLp9PhV4CGaMsFx7ZFaX1yEQbwNb8+85+U2HIHqjZ62u9KZ4lA1d58VVDGj2f7ObKkC+pjiuknljy5bvRVvg5pttXggJracFgMqouwwQFFCV3nFYSDS6h7ZIjUvbyMrqBN+u32RmtqVLp1by1JvRXSeemJ3HxtZGLbq7rff4zWXyYbelT6sR6J1tWIubRo5v3sXc8E1kurqKkcPqJdG4jqiUXOau2oSHhf7WCRwa0KNfvaQuMhm0Onnsi9tW2MzGieumscfuFIJsXJdXnac7jQUVb571GfxMrDeJ9v2GOPcbnlM8cttBFAw4IoINV6teKITUW6T8RpeDXzfQyxDDQaV0M1ZTab1Tj/f4EYDAXKfZ+QquWK3bgXJhxKghXIskZLdDhYMyP55T6QxLZY9wD2CxLrEbEhDQCEb+7R2LmIC95Rlku+W92b8eWAqNxf+vV7QRqBb/sV9w2mlxmmezYTP5VAs=

From f9cfec39a3efb70772f6d18129c34a473ba2b3c8 Mon Sep 17 00:00:00 2001
From: Jack Flintermann <jack@stripe.com>
Date: Thu, 21 Sep 2017 18:19:35 -0400
Subject: [PATCH 17/48] simplify travis

---
 .travis.yml | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index f4947d4..79609b4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,19 +13,6 @@ matrix:
         - set -o pipefail;
         - open -a "simulator" --args -CurrentDeviceUDID "$UUID"
         - xcodebuild -scheme Dwifft -destination "id=$UUID" test | xcpretty
-    - os: osx
-      language: objective-c
-      osx_image: xcode9
-      env:
-        - PLATFORM=tvOS NAME='Apple TV 1080p'
-      before_install:
-        export UUID=$(instruments -s | ruby -e "ARGF.each_line{ |ln| ln =~ /$NAME .* \[(.*)\]/; if \$1; puts(\$1); exit; end }");
-      before_script:
-        carthage bootstrap --platform $PLATFORM --configuration Debug
-      script:
-        - set -o pipefail;
-        - open -a "simulator" --args -CurrentDeviceUDID "$UUID"
-        - xcodebuild -scheme Dwifft -destination "id=$UUID" test | xcpretty
 env:
   global:
     secure: pUV8Ccwq1FWO8PxTHPQ3qZDUDhjNOisNVRyrziYR8x0ZQYjmVmnaLKaOHFP8co6rhBlFqReKUjoFYgRqP5QK1EkD6lqSZfnkrWqnZuLDNy0sn3/+4sLtvqAhBroAfjmmvb5GpY+Kkqfz8Lhdu6+1Z/a5xPhqNeDO3aDpa2vdBztb2qLIZnf2g8NFLOoNuR+ula868nIm858LCN1sqLp9PhV4CGaMsFx7ZFaX1yEQbwNb8+85+U2HIHqjZ62u9KZ4lA1d58VVDGj2f7ObKkC+pjiuknljy5bvRVvg5pttXggJracFgMqouwwQFFCV3nFYSDS6h7ZIjUvbyMrqBN+u32RmtqVLp1by1JvRXSeemJ3HxtZGLbq7rff4zWXyYbelT6sR6J1tWIubRo5v3sXc8E1kurqKkcPqJdG4jqiUXOau2oSHhf7WCRwa0KNfvaQuMhm0Onnsi9tW2MzGieumscfuFIJsXJdXnac7jQUVb571GfxMrDeJ9v2GOPcbnlM8cttBFAw4IoINV6teKITUW6T8RpeDXzfQyxDDQaV0M1ZTab1Tj/f4EYDAXKfZ+QquWK3bgXJhxKghXIskZLdDhYMyP55T6QxLZY9wD2CxLrEbEhDQCEb+7R2LmIC95Rlku+W92b8eWAqNxf+vV7QRqBb/sV9w2mlxmmezYTP5VAs=

From f224cce1f9be2263bc6e45cc3725d860504dacd4 Mon Sep 17 00:00:00 2001
From: Jack Flintermann <jack@stripe.com>
Date: Thu, 21 Sep 2017 18:20:40 -0400
Subject: [PATCH 18/48] podspec

---
 Dwifft.podspec | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Dwifft.podspec b/Dwifft.podspec
index b97fc43..dedf069 100644
--- a/Dwifft.podspec
+++ b/Dwifft.podspec
@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name = 'Dwifft'
-  s.version = '0.6.3'
+  s.version = '0.7'
   s.license = 'MIT'
   s.summary = 'Swift Diff'
   s.homepage = 'https://github.com/jflinter/Dwifft'

From b123166a32c4985749c5363c8b322e255a04e271 Mon Sep 17 00:00:00 2001
From: Jack Flintermann <jack@stripe.com>
Date: Mon, 25 Sep 2017 13:49:34 -0400
Subject: [PATCH 19/48] .swift-version

---
 .swift-version | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 .swift-version

diff --git a/.swift-version b/.swift-version
new file mode 100644
index 0000000..5186d07
--- /dev/null
+++ b/.swift-version
@@ -0,0 +1 @@
+4.0

From 3e246d187692818e54ed63684c172163ea94f8b7 Mon Sep 17 00:00:00 2001
From: Rik Chilvers <rikchilvers@fastmail.com>
Date: Wed, 27 Sep 2017 13:49:36 +0100
Subject: [PATCH 20/48] Hide SectionedTableViewDiffCalculator
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

NSTableView’s only support one section.
---
 Dwifft/Dwifft+AppKit.swift | 26 ++++++++++----------------
 1 file changed, 10 insertions(+), 16 deletions(-)

diff --git a/Dwifft/Dwifft+AppKit.swift b/Dwifft/Dwifft+AppKit.swift
index 7644108..21d4e34 100644
--- a/Dwifft/Dwifft+AppKit.swift
+++ b/Dwifft/Dwifft+AppKit.swift
@@ -84,9 +84,8 @@ public class AbstractDiffCalculator<Section: Equatable, Value: Equatable> {
     }
 }
 
-/// This class manages a `NSTableView`'s rows and sections. It will make the necessary calls to
-/// the table view to ensure that its UI is kept in sync with the contents of the `sectionedValues` property.
-public final class TableViewDiffCalculator<Section: Equatable, Value: Equatable>: AbstractDiffCalculator<Section, Value> {
+/// NSTableView does not support sections so we hide this from users.
+private final class SectionedTableViewDiffCalculator<Section: Equatable, Value: Equatable>: AbstractDiffCalculator<Section, Value> {
 
     /// The table view to be managed
     public weak var tableView: NSTableView?
@@ -161,16 +160,11 @@ public final class CollectionViewDiffCalculator<Section: Equatable, Value: Equat
 /// of `Int`s, define a `StringOrInt` enum that conforms to `Equatable`, and fill the `SectionedValues`
 /// that you use to drive your DiffCalculator up with those. Alternatively, if you are lazy, and your
 /// models all conform to `Hashable`, you can use a SimpleTableViewDiffCalculator instead.
-typealias SimpleTableViewDiffCalculator = TableViewDiffCalculator<AnyHashable, AnyHashable>
-
-/// See SimpleTableViewDiffCalculator for explanation
 typealias SimpleCollectionViewDiffCalculator = CollectionViewDiffCalculator<AnyHashable, AnyHashable>
 
-/// If your table view only has a single section, or you only want to power a single section of it with Dwifft,
-/// use a `SingleSectionTableViewDiffCalculator`. Note that this approach is not highly recommended, and you should
-/// do so only if it *really* doesn't make sense to just power your whole table with a `TableViewDiffCalculator`.
-/// You'll be less likely to mess up the index math :P
-public final class SingleSectionTableViewDiffCalculator<Value: Equatable> {
+/// This class manages a `NSTableView`'s rows. It will make the necessary
+/// calls to the table view to ensure that its UI is kept in sync with the contents of the `rows` property.
+public final class TableViewDiffCalculator<Value: Equatable> {
 
     /// The table view to be managed
     public weak var tableView: NSTableView?
@@ -199,7 +193,7 @@ public final class SingleSectionTableViewDiffCalculator<Value: Equatable> {
             return self.internalDiffCalculator.sectionedValues[self.sectionIndex].1
         }
         set {
-            self.internalDiffCalculator.sectionedValues = SingleSectionTableViewDiffCalculator.buildSectionedValues(values: newValue, sectionIndex: self.sectionIndex)
+            self.internalDiffCalculator.sectionedValues = TableViewDiffCalculator.buildSectionedValues(values: newValue, sectionIndex: self.sectionIndex)
         }
     }
 
@@ -211,7 +205,7 @@ public final class SingleSectionTableViewDiffCalculator<Value: Equatable> {
     ///   - sectionIndex: optional - all insertion/deletion calls will be made on this index.
     public init(tableView: NSTableView?, initialRows: [Value] = [], sectionIndex: Int = 0) {
         self.tableView = tableView
-        self.internalDiffCalculator = TableViewDiffCalculator(tableView: tableView, initialSectionedValues: SingleSectionTableViewDiffCalculator.buildSectionedValues(values: initialRows, sectionIndex: sectionIndex))
+        self.internalDiffCalculator = SectionedTableViewDiffCalculator(tableView: tableView, initialSectionedValues: TableViewDiffCalculator.buildSectionedValues(values: initialRows, sectionIndex: sectionIndex))
         self.sectionIndex = sectionIndex
     }
 
@@ -220,7 +214,7 @@ public final class SingleSectionTableViewDiffCalculator<Value: Equatable> {
         return SectionedValues(firstRows + [(sectionIndex, values)])
     }
 
-    private let internalDiffCalculator: TableViewDiffCalculator<Int, Value>
+    private let internalDiffCalculator: SectionedTableViewDiffCalculator<Int, Value>
 
 }
 
@@ -243,7 +237,7 @@ public final class SingleSectionCollectionViewDiffCalculator<Value: Equatable> {
             return self.internalDiffCalculator.sectionedValues[self.sectionIndex].1
         }
         set {
-            self.internalDiffCalculator.sectionedValues = SingleSectionTableViewDiffCalculator.buildSectionedValues(values: newValue, sectionIndex: self.sectionIndex)
+            self.internalDiffCalculator.sectionedValues = TableViewDiffCalculator.buildSectionedValues(values: newValue, sectionIndex: self.sectionIndex)
         }
     }
 
@@ -255,7 +249,7 @@ public final class SingleSectionCollectionViewDiffCalculator<Value: Equatable> {
     ///   - sectionIndex: optional - all insertion/deletion calls will be made on this index.
     public init(collectionView: NSCollectionView?, initialItems: [Value] = [], sectionIndex: Int = 0) {
         self.collectionView = collectionView
-        self.internalDiffCalculator = CollectionViewDiffCalculator(collectionView: collectionView, initialSectionedValues: SingleSectionTableViewDiffCalculator.buildSectionedValues(values: initialItems, sectionIndex: sectionIndex))
+        self.internalDiffCalculator = CollectionViewDiffCalculator(collectionView: collectionView, initialSectionedValues: TableViewDiffCalculator.buildSectionedValues(values: initialItems, sectionIndex: sectionIndex))
         self.sectionIndex = sectionIndex
     }
 

From eea1913e497e34e1e4452388d3e503ad3d87c2b0 Mon Sep 17 00:00:00 2001
From: Rik Chilvers <rikchilvers@fastmail.com>
Date: Wed, 27 Sep 2017 17:16:59 +0100
Subject: [PATCH 21/48] Add macOS tests, rename macOS framework

The macOS framework now builds as Dwifft.framework
---
 Cartfile.private                              |   2 +-
 Cartfile.resolved                             |   2 +-
 Dwifft.xcodeproj/project.pbxproj              | 159 ++++++-
 .../xcschemes/Dwifft-macOS.xcscheme           |  17 +-
 DwifftTests/DwifftTests-macOS.swift           | 416 ++++++++++++++++++
 5 files changed, 580 insertions(+), 16 deletions(-)
 create mode 100644 DwifftTests/DwifftTests-macOS.swift

diff --git a/Cartfile.private b/Cartfile.private
index ad6fa14..7fc4ebf 100644
--- a/Cartfile.private
+++ b/Cartfile.private
@@ -1 +1 @@
-github "jflinter/SwiftCheck" "8cb33343f45bc1b2f621011e096dbdd7e66787d6"
\ No newline at end of file
+github "typelift/SwiftCheck"
diff --git a/Cartfile.resolved b/Cartfile.resolved
index 1abd40f..97fb7bd 100644
--- a/Cartfile.resolved
+++ b/Cartfile.resolved
@@ -1 +1 @@
-github "jflinter/SwiftCheck" "8cb33343f45bc1b2f621011e096dbdd7e66787d6"
+github "typelift/SwiftCheck" "0.8.1"
diff --git a/Dwifft.xcodeproj/project.pbxproj b/Dwifft.xcodeproj/project.pbxproj
index e6aee10..d522ff0 100644
--- a/Dwifft.xcodeproj/project.pbxproj
+++ b/Dwifft.xcodeproj/project.pbxproj
@@ -17,10 +17,16 @@
 		0486A2651EA0A5B600D8093E /* SectionedValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0486A2641EA0A5B600D8093E /* SectionedValues.swift */; };
 		0486A2661EA0A64900D8093E /* SectionedValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0486A2641EA0A5B600D8093E /* SectionedValues.swift */; };
 		04AC329F1E88AEB000EF63DD /* SwiftCheck.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04AC329E1E88AEB000EF63DD /* SwiftCheck.framework */; };
+		380FFA411F7BF2E000AEF983 /* SwiftCheck.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 38D317731F7BF1E400A15CD7 /* SwiftCheck.framework */; };
 		382DEF461F487396007A8FD2 /* Dwifft_macOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 382DEF441F487395007A8FD2 /* Dwifft_macOS.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		382DEF4B1F487519007A8FD2 /* Dwifft.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041EBBAE1B8967C300E113B3 /* Dwifft.swift */; };
 		382DEF4C1F48751B007A8FD2 /* SectionedValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0486A2641EA0A5B600D8093E /* SectionedValues.swift */; };
 		382DEF4D1F48751C007A8FD2 /* Dwifft+AppKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 382DEF4A1F4873F9007A8FD2 /* Dwifft+AppKit.swift */; };
+		38E5B5D61F7BDB5400F33285 /* SectionedValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0486A2641EA0A5B600D8093E /* SectionedValues.swift */; };
+		38E5B5D71F7BDB5400F33285 /* Dwifft.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041EBBAE1B8967C300E113B3 /* Dwifft.swift */; };
+		38E5B5E91F7BDBA500F33285 /* Dwifft.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 382DEF421F487395007A8FD2 /* Dwifft.framework */; };
+		38E5B5EA1F7BDBBB00F33285 /* Dwifft+AppKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 382DEF4A1F4873F9007A8FD2 /* Dwifft+AppKit.swift */; };
+		38E5B5EB1F7BDBC700F33285 /* DwifftTests-macOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38E5B5E61F7BDB9800F33285 /* DwifftTests-macOS.swift */; };
 		94783CB11EFBA21900841579 /* Dwifft_tvOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 94783CAF1EFBA21900841579 /* Dwifft_tvOS.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		94783CB51EFBA25300841579 /* Dwifft.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041EBBAE1B8967C300E113B3 /* Dwifft.swift */; };
 		94783CB61EFBA25300841579 /* SectionedValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0486A2641EA0A5B600D8093E /* SectionedValues.swift */; };
@@ -35,6 +41,13 @@
 			remoteGlobalIDString = 041EBB911B89679200E113B3;
 			remoteInfo = Dwifft;
 		};
+		38E5B5E41F7BDB7300F33285 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 041EBB891B89679200E113B3 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 382DEF411F487395007A8FD2;
+			remoteInfo = "Dwifft-macOS";
+		};
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXFileReference section */
@@ -48,10 +61,13 @@
 		041EBBAF1B8967C300E113B3 /* Dwifft+UIKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Dwifft+UIKit.swift"; sourceTree = "<group>"; };
 		0486A2641EA0A5B600D8093E /* SectionedValues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionedValues.swift; sourceTree = "<group>"; };
 		04AC329E1E88AEB000EF63DD /* SwiftCheck.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftCheck.framework; path = Carthage/Build/iOS/SwiftCheck.framework; sourceTree = SOURCE_ROOT; };
-		382DEF421F487395007A8FD2 /* Dwifft_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Dwifft_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		382DEF421F487395007A8FD2 /* Dwifft.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Dwifft.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		382DEF441F487395007A8FD2 /* Dwifft_macOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Dwifft_macOS.h; sourceTree = "<group>"; };
 		382DEF451F487395007A8FD2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		382DEF4A1F4873F9007A8FD2 /* Dwifft+AppKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dwifft+AppKit.swift"; sourceTree = "<group>"; };
+		38D317731F7BF1E400A15CD7 /* SwiftCheck.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftCheck.framework; path = Carthage/Build/Mac/SwiftCheck.framework; sourceTree = SOURCE_ROOT; };
+		38E5B5E21F7BDB5400F33285 /* DwifftTests-macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "DwifftTests-macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
+		38E5B5E61F7BDB9800F33285 /* DwifftTests-macOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DwifftTests-macOS.swift"; sourceTree = "<group>"; };
 		94783CAD1EFBA21900841579 /* Dwifft_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Dwifft_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		94783CAF1EFBA21900841579 /* Dwifft_tvOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Dwifft_tvOS.h; sourceTree = "<group>"; };
 		94783CB01EFBA21900841579 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -81,6 +97,15 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		38E5B5DA1F7BDB5400F33285 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				38E5B5E91F7BDBA500F33285 /* Dwifft.framework in Frameworks */,
+				380FFA411F7BF2E000AEF983 /* SwiftCheck.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		94783CA91EFBA21900841579 /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
@@ -99,6 +124,7 @@
 				94783CAE1EFBA21900841579 /* Dwifft-tvOS */,
 				382DEF431F487395007A8FD2 /* Dwifft-macOS */,
 				041EBB931B89679200E113B3 /* Products */,
+				38E5B5E81F7BDBA500F33285 /* Frameworks */,
 			);
 			sourceTree = "<group>";
 		};
@@ -108,7 +134,8 @@
 				041EBB921B89679200E113B3 /* Dwifft.framework */,
 				041EBB9D1B89679200E113B3 /* DwifftTests.xctest */,
 				94783CAD1EFBA21900841579 /* Dwifft_tvOS.framework */,
-				382DEF421F487395007A8FD2 /* Dwifft_macOS.framework */,
+				382DEF421F487395007A8FD2 /* Dwifft.framework */,
+				38E5B5E21F7BDB5400F33285 /* DwifftTests-macOS.xctest */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -137,6 +164,7 @@
 		041EBBA11B89679200E113B3 /* DwifftTests */ = {
 			isa = PBXGroup;
 			children = (
+				38D317751F7BF21000A15CD7 /* macOS */,
 				041EBBA41B89679200E113B3 /* DwifftTests.swift */,
 				04AC329E1E88AEB000EF63DD /* SwiftCheck.framework */,
 				041EBBA21B89679200E113B3 /* Supporting Files */,
@@ -161,6 +189,22 @@
 			path = "Dwifft-macOS";
 			sourceTree = "<group>";
 		};
+		38D317751F7BF21000A15CD7 /* macOS */ = {
+			isa = PBXGroup;
+			children = (
+				38D317731F7BF1E400A15CD7 /* SwiftCheck.framework */,
+				38E5B5E61F7BDB9800F33285 /* DwifftTests-macOS.swift */,
+			);
+			name = macOS;
+			sourceTree = "<group>";
+		};
+		38E5B5E81F7BDBA500F33285 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
 		94783CAE1EFBA21900841579 /* Dwifft-tvOS */ = {
 			isa = PBXGroup;
 			children = (
@@ -252,9 +296,28 @@
 			);
 			name = "Dwifft-macOS";
 			productName = "Dwifft-macOS";
-			productReference = 382DEF421F487395007A8FD2 /* Dwifft_macOS.framework */;
+			productReference = 382DEF421F487395007A8FD2 /* Dwifft.framework */;
 			productType = "com.apple.product-type.framework";
 		};
+		38E5B5D21F7BDB5400F33285 /* DwifftTests-macOS */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 38E5B5DF1F7BDB5400F33285 /* Build configuration list for PBXNativeTarget "DwifftTests-macOS" */;
+			buildPhases = (
+				38E5B5D51F7BDB5400F33285 /* Sources */,
+				38E5B5DA1F7BDB5400F33285 /* Frameworks */,
+				38E5B5DD1F7BDB5400F33285 /* Resources */,
+				38E5B5DE1F7BDB5400F33285 /* ShellScript */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				38E5B5E51F7BDB7300F33285 /* PBXTargetDependency */,
+			);
+			name = "DwifftTests-macOS";
+			productName = DwifftTests;
+			productReference = 38E5B5E21F7BDB5400F33285 /* DwifftTests-macOS.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
 		94783CAC1EFBA21900841579 /* Dwifft-tvOS */ = {
 			isa = PBXNativeTarget;
 			buildConfigurationList = 94783CB41EFBA21900841579 /* Build configuration list for PBXNativeTarget "Dwifft-tvOS" */;
@@ -317,6 +380,7 @@
 				041EBB9C1B89679200E113B3 /* DwifftTests */,
 				94783CAC1EFBA21900841579 /* Dwifft-tvOS */,
 				382DEF411F487395007A8FD2 /* Dwifft-macOS */,
+				38E5B5D21F7BDB5400F33285 /* DwifftTests-macOS */,
 			);
 		};
 /* End PBXProject section */
@@ -343,6 +407,13 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		38E5B5DD1F7BDB5400F33285 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		94783CAB1EFBA21900841579 /* Resources */ = {
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -367,6 +438,20 @@
 			shellPath = /bin/sh;
 			shellScript = "/usr/local/bin/carthage copy-frameworks";
 		};
+		38E5B5DE1F7BDB5400F33285 /* ShellScript */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"$(SRCROOT)/Carthage/Build/Mac/SwiftCheck.framework",
+			);
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "/usr/local/bin/carthage copy-frameworks";
+		};
 /* End PBXShellScriptBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
@@ -401,6 +486,17 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		38E5B5D51F7BDB5400F33285 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				38E5B5EB1F7BDBC700F33285 /* DwifftTests-macOS.swift in Sources */,
+				38E5B5EA1F7BDBBB00F33285 /* Dwifft+AppKit.swift in Sources */,
+				38E5B5D61F7BDB5400F33285 /* SectionedValues.swift in Sources */,
+				38E5B5D71F7BDB5400F33285 /* Dwifft.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		94783CA81EFBA21900841579 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -419,6 +515,11 @@
 			target = 041EBB911B89679200E113B3 /* Dwifft */;
 			targetProxy = 041EBB9F1B89679200E113B3 /* PBXContainerItemProxy */;
 		};
+		38E5B5E51F7BDB7300F33285 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 382DEF411F487395007A8FD2 /* Dwifft-macOS */;
+			targetProxy = 38E5B5E41F7BDB7300F33285 /* PBXContainerItemProxy */;
+		};
 /* End PBXTargetDependency section */
 
 /* Begin XCBuildConfiguration section */
@@ -629,7 +730,7 @@
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
 				MACOSX_DEPLOYMENT_TARGET = 10.12;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.Dwifft-macOS";
-				PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+				PRODUCT_NAME = Dwifft;
 				SDKROOT = macosx;
 				SKIP_INSTALL = YES;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
@@ -660,7 +761,7 @@
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
 				MACOSX_DEPLOYMENT_TARGET = 10.12;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.Dwifft-macOS";
-				PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+				PRODUCT_NAME = Dwifft;
 				SDKROOT = macosx;
 				SKIP_INSTALL = YES;
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
@@ -668,6 +769,45 @@
 			};
 			name = Release;
 		};
+		38E5B5E01F7BDB5400F33285 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Carthage/Build/Mac",
+				);
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				INFOPLIST_FILE = "DwifftTests copy-Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = macosx10.13;
+				SWIFT_SWIFT3_OBJC_INFERENCE = On;
+				SWIFT_VERSION = 4.0;
+			};
+			name = Debug;
+		};
+		38E5B5E11F7BDB5400F33285 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Carthage/Build/Mac",
+				);
+				INFOPLIST_FILE = "DwifftTests copy-Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = macosx10.13;
+				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+				SWIFT_SWIFT3_OBJC_INFERENCE = On;
+				SWIFT_VERSION = 4.0;
+			};
+			name = Release;
+		};
 		94783CB21EFBA21900841579 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
@@ -772,6 +912,15 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
+		38E5B5DF1F7BDB5400F33285 /* Build configuration list for PBXNativeTarget "DwifftTests-macOS" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				38E5B5E01F7BDB5400F33285 /* Debug */,
+				38E5B5E11F7BDB5400F33285 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 		94783CB41EFBA21900841579 /* Build configuration list for PBXNativeTarget "Dwifft-tvOS" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
diff --git a/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-macOS.xcscheme b/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-macOS.xcscheme
index c31dfbf..f1717bf 100644
--- a/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-macOS.xcscheme
+++ b/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-macOS.xcscheme
@@ -15,7 +15,7 @@
             <BuildableReference
                BuildableIdentifier = "primary"
                BlueprintIdentifier = "382DEF411F487395007A8FD2"
-               BuildableName = "Dwifft_macOS.framework"
+               BuildableName = "Dwifft.framework"
                BlueprintName = "Dwifft-macOS"
                ReferencedContainer = "container:Dwifft.xcodeproj">
             </BuildableReference>
@@ -27,16 +27,15 @@
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       language = ""
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      codeCoverageEnabled = "YES">
+      shouldUseLaunchSchemeArgsEnv = "YES">
       <Testables>
          <TestableReference
             skipped = "NO">
             <BuildableReference
                BuildableIdentifier = "primary"
-               BlueprintIdentifier = "041EBB9C1B89679200E113B3"
-               BuildableName = "DwifftTests.xctest"
-               BlueprintName = "DwifftTests"
+               BlueprintIdentifier = "38E5B5D21F7BDB5400F33285"
+               BuildableName = "DwifftTests-macOS.xctest"
+               BlueprintName = "DwifftTests-macOS"
                ReferencedContainer = "container:Dwifft.xcodeproj">
             </BuildableReference>
          </TestableReference>
@@ -45,7 +44,7 @@
          <BuildableReference
             BuildableIdentifier = "primary"
             BlueprintIdentifier = "382DEF411F487395007A8FD2"
-            BuildableName = "Dwifft_macOS.framework"
+            BuildableName = "Dwifft.framework"
             BlueprintName = "Dwifft-macOS"
             ReferencedContainer = "container:Dwifft.xcodeproj">
          </BuildableReference>
@@ -68,7 +67,7 @@
          <BuildableReference
             BuildableIdentifier = "primary"
             BlueprintIdentifier = "382DEF411F487395007A8FD2"
-            BuildableName = "Dwifft_macOS.framework"
+            BuildableName = "Dwifft.framework"
             BlueprintName = "Dwifft-macOS"
             ReferencedContainer = "container:Dwifft.xcodeproj">
          </BuildableReference>
@@ -86,7 +85,7 @@
          <BuildableReference
             BuildableIdentifier = "primary"
             BlueprintIdentifier = "382DEF411F487395007A8FD2"
-            BuildableName = "Dwifft_macOS.framework"
+            BuildableName = "Dwifft.framework"
             BlueprintName = "Dwifft-macOS"
             ReferencedContainer = "container:Dwifft.xcodeproj">
          </BuildableReference>
diff --git a/DwifftTests/DwifftTests-macOS.swift b/DwifftTests/DwifftTests-macOS.swift
new file mode 100644
index 0000000..71ea1c3
--- /dev/null
+++ b/DwifftTests/DwifftTests-macOS.swift
@@ -0,0 +1,416 @@
+//
+//  DwifftTests-macOS.swift
+//  DwifftTests
+//
+//  Created by Rik Chilvers on 27/09/2017.
+//  Copyright © 2017 jflinter. All rights reserved.
+//
+
+import Cocoa
+import XCTest
+import SwiftCheck
+
+struct SectionedValuesWrapper: Arbitrary {
+  let values: SectionedValues<Int, Int>
+
+  public static var arbitrary: Gen<SectionedValuesWrapper> {
+    let arrayOfNumbers = Gen<Int>.fromElements(in: 0...10).proliferate.suchThat({ $0.count <= 100 })
+    return arrayOfNumbers.map { array in
+      return array.map { i in
+        return (i, arrayOfNumbers.generate)
+      }
+      }.map { val in
+        return SectionedValuesWrapper(values: SectionedValues<Int, Int>(val))
+    }
+  }
+}
+
+class DwifftSwiftCheckTests: XCTestCase {
+
+  func testDiff() {
+    property("Diffing two arrays, then applying the diff to the first, yields the second") <- forAll { (a1 : ArrayOf<Int>, a2 : ArrayOf<Int>) in
+      let diff = Dwifft.diff(a1.getArray, a2.getArray)
+      return (Dwifft.apply(diff: diff, toArray: a1.getArray) == a2.getArray) <?> "diff applies"
+    }
+  }
+
+  func test2DDiff() {
+    property("Diffing two 2D arrays, then applying the diff to the first, yields the second") <- forAll { (lhs : SectionedValuesWrapper, rhs: SectionedValuesWrapper) in
+      let diff = Dwifft.diff(lhs: lhs.values, rhs: rhs.values)
+      return (Dwifft.apply(diff: diff, toSectionedValues: lhs.values) == rhs.values) <?> "2d diff applies"
+    }
+  }
+
+  func testAppKit2D() {
+
+    class DataSource: NSObject, NSTableViewDataSource {
+      let diffCalculator: TableViewDiffCalculator<Int>
+
+      init(_ diffCalculator: TableViewDiffCalculator<Int>) {
+        self.diffCalculator = diffCalculator
+      }
+
+      func numberOfRows(in tableView: NSTableView) -> Int {
+        return self.diffCalculator.rows.count
+      }
+
+      func tableView(_ tableView: NSTableView, cellForRowAt indexPath: IndexPath) -> NSTableCellView {
+        return NSTableCellView()
+      }
+    }
+
+    property("Updating a TableViewDiffCalculator never raises an exception") <- forAll { (lhs : SectionedValuesWrapper, rhs: SectionedValuesWrapper) in
+      let tableView = NSTableView()
+      let diffCalculator = TableViewDiffCalculator(tableView: tableView,
+                                                   initialRows: lhs.values.sectionsAndValues.first?.1 ?? [])
+      let dataSource = DataSource(diffCalculator)
+      tableView.dataSource = dataSource
+      tableView.reloadData()
+      diffCalculator.rows = rhs.values.sectionsAndValues.first?.1 ?? []
+
+      return true <?> "no exception was raised"
+    }
+  }
+}
+
+class DwifftTests: XCTestCase {
+
+  struct TestCase {
+    let array1: [Character]
+    let array2: [Character]
+    let expectedDiff: String
+    init(_ a: String, _ b: String, _ expectedDiff: String) {
+      self.array1 = Array(a.characters)
+      self.array2 = Array(b.characters)
+      self.expectedDiff = expectedDiff
+    }
+  }
+
+  func testDiff() {
+    let tests: [TestCase] = [
+      TestCase("1234", "23", "-4@3-1@0"),
+      TestCase("0125890", "4598310", "-8@4-2@2-1@1-0@0+4@0+8@3+3@4+1@5"),
+      TestCase("BANANA", "KATANA", "-N@2-B@0+K@0+T@2"),
+      TestCase("1234", "1224533324", "+2@2+4@3+5@4+3@6+3@7+2@8"),
+      TestCase("thisisatest", "testing123testing", "-a@6-s@5-i@2-h@1+e@1+t@3+n@5+g@6+1@7+2@8+3@9+i@14+n@15+g@16"),
+      TestCase("HUMAN", "CHIMPANZEE", "-U@1+C@0+I@2+P@4+Z@7+E@8+E@9"),
+      ]
+
+    for test in tests {
+      let diff = Dwifft.diff(test.array1, test.array2)
+      let printableDiff = diff.map({ $0.debugDescription }).joined(separator: "")
+      XCTAssertEqual(printableDiff, test.expectedDiff, "incorrect diff")
+    }
+  }
+
+  func testDiffBenchmark() {
+    let a: [Int] = (0...1000).map({ _ in Int(arc4random_uniform(100)) }).filter({ _ in arc4random_uniform(2) == 0})
+    let b: [Int] = (0...1000).map({ _ in Int(arc4random_uniform(100)) }).filter({ _ in arc4random_uniform(2) == 0})
+    measure {
+      let _ = Dwifft.diff(a, b)
+    }
+  }
+
+  func test2D() {
+    let testCases: [([(String, [Int])], [(String, [Int])], String)] = [
+      (
+        [("a", [0, 1]), ("b", [2, 3, 4])],
+        [("b", [2])],
+        "[d(1 2), d(1 1), ds(0)]"
+      ),
+      (
+        [("a", []), ("b", [])],
+        [],
+        "[ds(1), ds(0)]"
+      ),
+      (
+        [],
+        [("a", []), ("b", [])],
+        "[is(0), is(1)]"
+      ),
+      (
+        [],
+        [("a", [1, 2]), ("b", [3, 4, 5])],
+        "[is(0), is(1), i(0 0), i(0 1), i(1 0), i(1 1), i(1 2)]"
+      ),
+      (
+        [],
+        [],
+        "[]"
+      ),
+      (
+        [("a", [1]), ("b", []), ("c", [])],
+        [("a", [1])],
+        "[ds(2), ds(1)]"
+      ),
+      (
+        [("a", []), ("b", [1]), ("c", [])],
+        [("a", []), ("b", [2]), ("c", [])],
+        "[d(1 0), i(1 0)]"
+      ),
+      (
+        [("a", [1]), ("b", []), ("c", [])],
+        [("a", []), ("b", [1]), ("c", [])],
+        "[d(0 0), i(1 0)]"
+      ),
+      (
+        [("a", [1]), ("b", []), ("c", [])],
+        [("q", []), ("a", [1])],
+        "[ds(2), ds(1), is(0)]"
+      ),
+      (
+        [("a", [1]), ("b", []), ("c", [])],
+        [("q", []), ("a", [1, 2])],
+        "[ds(2), ds(1), is(0), i(1 1)]"
+      ),
+      (
+        [("a", [1])],
+        [("q", []), ("a", [1])],
+        "[is(0)]"
+      ),
+      (
+        [("a", [1, 2]), ("b", [3, 4])],
+        [("a", [1, 2, 3, 4])],
+        "[ds(1), i(0 2), i(0 3)]"
+      ),
+      (
+        [("a", [1, 2, 3]), ("b", [4, 5]), ("c", [])],
+        [("q", []), ("a", [1, 2]), ("b", [3, 4])],
+        "[d(0 2), d(1 1), ds(2), is(0), i(2 0)]"
+      ),
+      ]
+    for (lhs, rhs, expected) in testCases {
+      let mappedLhs = SectionedValues(lhs.map { ($0, $1) })
+      let mappedRhs = SectionedValues(rhs.map { ($0, $1) })
+      XCTAssertEqual(Dwifft.diff(lhs: mappedLhs, rhs: mappedRhs).debugDescription, expected)
+    }
+  }
+
+  func test2DBenchmark() {
+    let n: Int = 70
+    let a: [(Int, [Int])] = (0...n).flatMap { (i: Int) -> (Int, [Int])? in
+      guard arc4random_uniform(2) == 0 else { return nil }
+      let value: [Int] = (0...arc4random_uniform(UInt32(n))).map { _ in Int(arc4random_uniform(100)) }
+      return (i, value)
+    }
+    let b: [(Int, [Int])] = (0...n).flatMap { (i: Int) -> (Int, [Int])? in
+      guard arc4random_uniform(2) == 0 else { return nil }
+      let value: [Int] = (0...arc4random_uniform(UInt32(n))).map { _ in Int(arc4random_uniform(100)) }
+      return (i, value)
+    }
+    let lhs = SectionedValues(a)
+    let rhs = SectionedValues(b)
+    measure {
+      let _ = Dwifft.diff(lhs: lhs, rhs: rhs)
+    }
+  }
+
+  func testSectionedValues() {
+    XCTAssertEqual(SectionedValues(values: [1,2,3,11,12,13,21,22,23], valueToSection: { i in
+      return i % 10
+    }, sortSections: {a, b in
+      return a < b
+    }, sortValues: {a, b in
+      return a < b
+    }), SectionedValues([(1, [1, 11, 21]), (2, [2, 12, 22]), (3, [3, 13, 23])]))
+
+    XCTAssertEqual(SectionedValues(values: [1,2,3,11,12,13,21,22,23], valueToSection: { i in
+      return i % 10
+    }, sortSections: {a, b in
+      return b < a
+    }, sortValues: {a, b in
+      return a < b
+    }), SectionedValues([(3, [3, 13, 23]), (2, [2, 12, 22]), (1, [1, 11, 21])]))
+
+    XCTAssertEqual(SectionedValues(values: [1,2,3,11,12,13,21,22,23], valueToSection: { i in
+      return i % 10
+    }, sortSections: {a, b in
+      return a < b
+    }, sortValues: {a, b in
+      return b < a
+    }), SectionedValues([(1, [21, 11, 1]), (2, [22, 12, 2]), (3, [23, 13, 3])]))
+
+    XCTAssertEqual(SectionedValues(values: [1,2,3,11,12,13,21,22,23], valueToSection: { i in
+      return i % 10
+    }, sortSections: {a, b in
+      return b < a
+    }, sortValues: {a, b in
+      return b < a
+    }), SectionedValues([(3, [23, 13, 3]), (2, [22, 12, 2]), (1, [21, 11, 1])]))
+  }
+
+  func testTableViewDiffCalculator() {
+
+    class TestTableView: NSTableView {
+
+      let insertionExpectations: [Int: XCTestExpectation]
+      let deletionExpectations: [Int: XCTestExpectation]
+
+      init(insertionExpectations: [Int: XCTestExpectation], deletionExpectations: [Int: XCTestExpectation]) {
+        self.insertionExpectations = insertionExpectations
+        self.deletionExpectations = deletionExpectations
+        super.init(frame: CGRect.zero)
+      }
+
+      required init?(coder aDecoder: NSCoder) {
+        fatalError("not implemented")
+      }
+
+      override func insertRows(at indexes: IndexSet, withAnimation animation: NSTableView.AnimationOptions) {
+        XCTAssertEqual(animation, NSTableView.AnimationOptions.slideLeft, "incorrect insertion animation")
+        for index in indexes {
+          self.insertionExpectations[index]!.fulfill()
+        }
+      }
+
+      override func removeRows(at indexes: IndexSet, withAnimation animation: NSTableView.AnimationOptions) {
+        XCTAssertEqual(animation, NSTableView.AnimationOptions.slideRight, "incorrect insertion animation")
+        for index in indexes {
+          self.deletionExpectations[index]!.fulfill()
+        }
+      }
+
+    }
+
+    class TestViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate {
+
+      let tableView: TestTableView
+      let diffCalculator: TableViewDiffCalculator<Int>
+      var rows: [Int] {
+        didSet {
+          self.diffCalculator.rows = rows
+        }
+      }
+
+      init(tableView: TestTableView, rows: [Int]) {
+        self.tableView = tableView
+        self.diffCalculator = TableViewDiffCalculator<Int>(tableView: tableView, initialRows: rows)
+        self.diffCalculator.insertionAnimation = .slideLeft
+        self.diffCalculator.deletionAnimation = .slideRight
+        self.rows = rows
+        super.init(nibName: nil, bundle: nil)
+        self.tableView.dataSource = self
+        self.tableView.delegate = self
+      }
+
+      required init?(coder aDecoder: NSCoder) {
+        fatalError("not implemented")
+      }
+
+      func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
+        return NSTableCellView()
+      }
+
+      func numberOfRows(in tableView: NSTableView) -> Int {
+        return self.diffCalculator.rows.count
+      }
+
+    }
+
+    var insertionExpectations: [Int: XCTestExpectation] = [:]
+    for i in [0, 3, 4, 5] {
+      let x: XCTestExpectation = expectation(description: "+\(i)")
+      insertionExpectations[i] = x
+    }
+
+    var deletionExpectations: [Int: XCTestExpectation] = [:]
+    for i in [0, 1, 2, 4] {
+      let x: XCTestExpectation = expectation(description: "+\(i)")
+      deletionExpectations[i] = x
+    }
+
+    let tableView = TestTableView(insertionExpectations: insertionExpectations, deletionExpectations: deletionExpectations)
+    let viewController = TestViewController(tableView: tableView, rows: [0, 1, 2, 5, 8, 9, 0])
+    viewController.rows = [4, 5, 9, 8, 3, 1, 0]
+    waitForExpectations(timeout: 1.0, handler: nil)
+  }
+
+  func testCollectionViewDiffCalculator() {
+
+    class TestCollectionView: NSCollectionView {
+
+      let insertionExpectations: [Int: XCTestExpectation]
+      let deletionExpectations: [Int: XCTestExpectation]
+
+      init(insertionExpectations: [Int: XCTestExpectation], deletionExpectations: [Int: XCTestExpectation]) {
+        self.insertionExpectations = insertionExpectations
+        self.deletionExpectations = deletionExpectations
+        super.init(frame: CGRect.zero)
+        self.collectionViewLayout = NSCollectionViewFlowLayout()
+      }
+
+      required init?(coder aDecoder: NSCoder) {
+        fatalError("not implemented")
+      }
+
+      override func insertItems(at indexPaths: Set<IndexPath>) {
+        super.insertItems(at: indexPaths)
+        for indexPath in indexPaths {
+          self.insertionExpectations[(indexPath as NSIndexPath).item]!.fulfill()
+        }
+      }
+
+      override func deleteItems(at indexPaths: Set<IndexPath>) {
+        super.deleteItems(at: indexPaths)
+        for indexPath in indexPaths {
+          self.deletionExpectations[(indexPath as NSIndexPath).item]!.fulfill()
+        }
+      }
+
+    }
+
+    class TestViewController: NSViewController, NSCollectionViewDataSource {
+      let itemIdentifier = NSUserInterfaceItemIdentifier("TestItem")
+      let testCollectionView: TestCollectionView
+      let diffCalculator: CollectionViewDiffCalculator<Int, Int>
+      var rows: [Int] {
+        didSet {
+          self.diffCalculator.sectionedValues = SectionedValues([(0, rows)])
+        }
+      }
+
+      init(collectionView: TestCollectionView, rows: [Int]) {
+        self.testCollectionView = collectionView
+        self.diffCalculator = CollectionViewDiffCalculator<Int, Int>(collectionView: self.testCollectionView, initialSectionedValues: SectionedValues([(0, rows)]))
+        self.rows = rows
+        super.init(nibName: nil, bundle: nil)
+
+        collectionView.register(NSCollectionViewItem.self, forItemWithIdentifier: self.itemIdentifier)
+        collectionView.dataSource = self
+      }
+
+      required init?(coder aDecoder: NSCoder) {
+        fatalError("not implemented")
+      }
+
+      func numberOfSections(in collectionView: NSCollectionView) -> Int {
+        return diffCalculator.numberOfSections()
+      }
+
+      func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
+        return diffCalculator.numberOfObjects(inSection: section)
+      }
+
+      func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
+        return collectionView.makeItem(withIdentifier: self.itemIdentifier, for: indexPath)
+      }
+    }
+
+    var insertionExpectations: [Int: XCTestExpectation] = [:]
+    for i in [0, 3, 4, 5] {
+      let x: XCTestExpectation = expectation(description: "+\(i)")
+      insertionExpectations[i] = x
+    }
+
+    var deletionExpectations: [Int: XCTestExpectation] = [:]
+    for i in [0, 1, 2, 4] {
+      let x: XCTestExpectation = expectation(description: "+\(i)")
+      deletionExpectations[i] = x
+    }
+
+    let collectionView = TestCollectionView(insertionExpectations: insertionExpectations, deletionExpectations: deletionExpectations)
+    let viewController = TestViewController(collectionView: collectionView, rows: [0, 1, 2, 5, 8, 9, 0])
+    viewController.rows = [4, 5, 9, 8, 3, 1, 0]
+    waitForExpectations(timeout: 1.0, handler: nil)
+  }
+}

From 3e0ba7b2ca5d1437f7b429e8e68749e047a22922 Mon Sep 17 00:00:00 2001
From: Rik Chilvers <rikchilvers@fastmail.com>
Date: Wed, 27 Sep 2017 17:23:10 +0100
Subject: [PATCH 22/48] add missing info.plist

---
 Dwifft.xcodeproj/project.pbxproj |  6 ++++--
 DwifftTests/macOS Info.plist     | 24 ++++++++++++++++++++++++
 2 files changed, 28 insertions(+), 2 deletions(-)
 create mode 100644 DwifftTests/macOS Info.plist

diff --git a/Dwifft.xcodeproj/project.pbxproj b/Dwifft.xcodeproj/project.pbxproj
index d522ff0..7f909a7 100644
--- a/Dwifft.xcodeproj/project.pbxproj
+++ b/Dwifft.xcodeproj/project.pbxproj
@@ -61,6 +61,7 @@
 		041EBBAF1B8967C300E113B3 /* Dwifft+UIKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Dwifft+UIKit.swift"; sourceTree = "<group>"; };
 		0486A2641EA0A5B600D8093E /* SectionedValues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionedValues.swift; sourceTree = "<group>"; };
 		04AC329E1E88AEB000EF63DD /* SwiftCheck.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftCheck.framework; path = Carthage/Build/iOS/SwiftCheck.framework; sourceTree = SOURCE_ROOT; };
+		380FFA421F7C072D00AEF983 /* macOS Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "macOS Info.plist"; sourceTree = "<group>"; };
 		382DEF421F487395007A8FD2 /* Dwifft.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Dwifft.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		382DEF441F487395007A8FD2 /* Dwifft_macOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Dwifft_macOS.h; sourceTree = "<group>"; };
 		382DEF451F487395007A8FD2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -194,6 +195,7 @@
 			children = (
 				38D317731F7BF1E400A15CD7 /* SwiftCheck.framework */,
 				38E5B5E61F7BDB9800F33285 /* DwifftTests-macOS.swift */,
+				380FFA421F7C072D00AEF983 /* macOS Info.plist */,
 			);
 			name = macOS;
 			sourceTree = "<group>";
@@ -780,7 +782,7 @@
 					"DEBUG=1",
 					"$(inherited)",
 				);
-				INFOPLIST_FILE = "DwifftTests copy-Info.plist";
+				INFOPLIST_FILE = "DwifftTests/macOS Info.plist";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -797,7 +799,7 @@
 					"$(inherited)",
 					"$(PROJECT_DIR)/Carthage/Build/Mac",
 				);
-				INFOPLIST_FILE = "DwifftTests copy-Info.plist";
+				INFOPLIST_FILE = "DwifftTests/macOS Info.plist";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
diff --git a/DwifftTests/macOS Info.plist b/DwifftTests/macOS Info.plist
new file mode 100644
index 0000000..ba72822
--- /dev/null
+++ b/DwifftTests/macOS Info.plist	
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>BNDL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>

From bd39fa308404f32bb76684dbafa3aa3ba748bd1a Mon Sep 17 00:00:00 2001
From: Rik Chilvers <rikchilvers@fastmail.com>
Date: Fri, 3 Nov 2017 18:34:02 +0000
Subject: [PATCH 23/48] pin macOS deployment target

---
 Dwifft.podspec                   | 1 +
 Dwifft.xcodeproj/project.pbxproj | 8 ++++----
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/Dwifft.podspec b/Dwifft.podspec
index dedf069..fe336b6 100644
--- a/Dwifft.podspec
+++ b/Dwifft.podspec
@@ -10,6 +10,7 @@ Pod::Spec.new do |s|
 
   s.ios.deployment_target = '8.0'
   s.tvos.deployment_target = '9.0'
+  s.osx.deployment_target = '10.11'
 
   s.source_files = 'Dwifft/*.swift'
 
diff --git a/Dwifft.xcodeproj/project.pbxproj b/Dwifft.xcodeproj/project.pbxproj
index 7f909a7..25740a3 100644
--- a/Dwifft.xcodeproj/project.pbxproj
+++ b/Dwifft.xcodeproj/project.pbxproj
@@ -730,10 +730,10 @@
 				INFOPLIST_FILE = "Dwifft-macOS/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
-				MACOSX_DEPLOYMENT_TARGET = 10.12;
+				MACOSX_DEPLOYMENT_TARGET = 10.11;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.Dwifft-macOS";
 				PRODUCT_NAME = Dwifft;
-				SDKROOT = macosx;
+				SDKROOT = macosx10.13;
 				SKIP_INSTALL = YES;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -761,10 +761,10 @@
 				INFOPLIST_FILE = "Dwifft-macOS/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
-				MACOSX_DEPLOYMENT_TARGET = 10.12;
+				MACOSX_DEPLOYMENT_TARGET = 10.11;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.Dwifft-macOS";
 				PRODUCT_NAME = Dwifft;
-				SDKROOT = macosx;
+				SDKROOT = macosx10.13;
 				SKIP_INSTALL = YES;
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
 				SWIFT_VERSION = 4.0;

From 85915a39eb4b4465043fb208465abeebab31249b Mon Sep 17 00:00:00 2001
From: Rik Chilvers <rikchilvers@fastmail.com>
Date: Sat, 4 Nov 2017 09:52:52 +0000
Subject: [PATCH 24/48] =?UTF-8?q?update=20for=20Swift=204=E2=80=99s=20Stri?=
 =?UTF-8?q?ng=20changes?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 DwifftTests/DwifftTests-macOS.swift | 4 ++--
 DwifftTests/DwifftTests.swift       | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/DwifftTests/DwifftTests-macOS.swift b/DwifftTests/DwifftTests-macOS.swift
index 71ea1c3..2267a5b 100644
--- a/DwifftTests/DwifftTests-macOS.swift
+++ b/DwifftTests/DwifftTests-macOS.swift
@@ -80,8 +80,8 @@ class DwifftTests: XCTestCase {
     let array2: [Character]
     let expectedDiff: String
     init(_ a: String, _ b: String, _ expectedDiff: String) {
-      self.array1 = Array(a.characters)
-      self.array2 = Array(b.characters)
+      self.array1 = a.map { $0 }
+      self.array2 = b.map { $0 }
       self.expectedDiff = expectedDiff
     }
   }
diff --git a/DwifftTests/DwifftTests.swift b/DwifftTests/DwifftTests.swift
index ec22da5..c758332 100644
--- a/DwifftTests/DwifftTests.swift
+++ b/DwifftTests/DwifftTests.swift
@@ -101,8 +101,8 @@ class DwifftTests: XCTestCase {
         let array2: [Character]
         let expectedDiff: String
         init(_ a: String, _ b: String, _ expectedDiff: String) {
-            self.array1 = Array(a.characters)
-            self.array2 = Array(b.characters)
+            self.array1 = a.map { $0 }
+            self.array2 = b.map { $0 }
             self.expectedDiff = expectedDiff
         }
     }

From 8c8b0f0df873c02dde8404c7321e023f9138e977 Mon Sep 17 00:00:00 2001
From: Rik Chilvers <rikchilvers@fastmail.com>
Date: Sat, 4 Nov 2017 10:12:30 +0000
Subject: [PATCH 25/48] disable swift3 objc inference

---
 Dwifft.xcodeproj/project.pbxproj | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/Dwifft.xcodeproj/project.pbxproj b/Dwifft.xcodeproj/project.pbxproj
index 25740a3..ae8bcc6 100644
--- a/Dwifft.xcodeproj/project.pbxproj
+++ b/Dwifft.xcodeproj/project.pbxproj
@@ -647,7 +647,7 @@
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
-				SWIFT_SWIFT3_OBJC_INFERENCE = On;
+				SWIFT_SWIFT3_OBJC_INFERENCE = Default;
 				SWIFT_VERSION = 4.0;
 			};
 			name = Debug;
@@ -667,7 +667,7 @@
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
-				SWIFT_SWIFT3_OBJC_INFERENCE = On;
+				SWIFT_SWIFT3_OBJC_INFERENCE = Default;
 				SWIFT_VERSION = 4.0;
 			};
 			name = Release;
@@ -687,7 +687,7 @@
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SWIFT_SWIFT3_OBJC_INFERENCE = On;
+				SWIFT_SWIFT3_OBJC_INFERENCE = Default;
 				SWIFT_VERSION = 4.0;
 			};
 			name = Debug;
@@ -704,7 +704,7 @@
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
-				SWIFT_SWIFT3_OBJC_INFERENCE = On;
+				SWIFT_SWIFT3_OBJC_INFERENCE = Default;
 				SWIFT_VERSION = 4.0;
 			};
 			name = Release;
@@ -787,7 +787,7 @@
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = macosx10.13;
-				SWIFT_SWIFT3_OBJC_INFERENCE = On;
+				SWIFT_SWIFT3_OBJC_INFERENCE = Default;
 				SWIFT_VERSION = 4.0;
 			};
 			name = Debug;
@@ -805,7 +805,7 @@
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = macosx10.13;
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
-				SWIFT_SWIFT3_OBJC_INFERENCE = On;
+				SWIFT_SWIFT3_OBJC_INFERENCE = Default;
 				SWIFT_VERSION = 4.0;
 			};
 			name = Release;

From 2ac80df7208b09ac93a578ae2c0a5a3a3749af9c Mon Sep 17 00:00:00 2001
From: Rik Chilvers <rikchilvers@fastmail.com>
Date: Sat, 4 Nov 2017 10:14:13 +0000
Subject: [PATCH 26/48] update tvOS scheme to swift 4

---
 Dwifft.xcodeproj/project.pbxproj | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/Dwifft.xcodeproj/project.pbxproj b/Dwifft.xcodeproj/project.pbxproj
index ae8bcc6..35a5c60 100644
--- a/Dwifft.xcodeproj/project.pbxproj
+++ b/Dwifft.xcodeproj/project.pbxproj
@@ -363,6 +363,7 @@
 					};
 					94783CAC1EFBA21900841579 = {
 						CreatedOnToolsVersion = 9.0;
+						LastSwiftMigration = 0910;
 					};
 				};
 			};
@@ -837,7 +838,8 @@
 				SDKROOT = appletvos;
 				SKIP_INSTALL = YES;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
-				SWIFT_VERSION = 3.0;
+				SWIFT_SWIFT3_OBJC_INFERENCE = Default;
+				SWIFT_VERSION = 4.0;
 				TARGETED_DEVICE_FAMILY = 3;
 				TVOS_DEPLOYMENT_TARGET = 9.0;
 			};
@@ -869,7 +871,8 @@
 				SDKROOT = appletvos;
 				SKIP_INSTALL = YES;
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
-				SWIFT_VERSION = 3.0;
+				SWIFT_SWIFT3_OBJC_INFERENCE = Default;
+				SWIFT_VERSION = 4.0;
 				TARGETED_DEVICE_FAMILY = 3;
 				TVOS_DEPLOYMENT_TARGET = 9.0;
 			};

From f9d665902410e2bc8ee3972e6c406d581e51d167 Mon Sep 17 00:00:00 2001
From: Rik Chilvers <rikchilvers@fastmail.com>
Date: Sat, 4 Nov 2017 10:31:17 +0000
Subject: [PATCH 27/48] remove unnecessary build stages

---
 Dwifft.xcodeproj/project.pbxproj | 23 -----------------------
 1 file changed, 23 deletions(-)

diff --git a/Dwifft.xcodeproj/project.pbxproj b/Dwifft.xcodeproj/project.pbxproj
index 35a5c60..3fb299a 100644
--- a/Dwifft.xcodeproj/project.pbxproj
+++ b/Dwifft.xcodeproj/project.pbxproj
@@ -290,7 +290,6 @@
 				382DEF3D1F487395007A8FD2 /* Sources */,
 				382DEF3E1F487395007A8FD2 /* Frameworks */,
 				382DEF3F1F487395007A8FD2 /* Headers */,
-				382DEF401F487395007A8FD2 /* Resources */,
 			);
 			buildRules = (
 			);
@@ -308,7 +307,6 @@
 				38E5B5D51F7BDB5400F33285 /* Sources */,
 				38E5B5DA1F7BDB5400F33285 /* Frameworks */,
 				38E5B5DD1F7BDB5400F33285 /* Resources */,
-				38E5B5DE1F7BDB5400F33285 /* ShellScript */,
 			);
 			buildRules = (
 			);
@@ -403,13 +401,6 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
-		382DEF401F487395007A8FD2 /* Resources */ = {
-			isa = PBXResourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
 		38E5B5DD1F7BDB5400F33285 /* Resources */ = {
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -441,20 +432,6 @@
 			shellPath = /bin/sh;
 			shellScript = "/usr/local/bin/carthage copy-frameworks";
 		};
-		38E5B5DE1F7BDB5400F33285 /* ShellScript */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputPaths = (
-				"$(SRCROOT)/Carthage/Build/Mac/SwiftCheck.framework",
-			);
-			outputPaths = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "/usr/local/bin/carthage copy-frameworks";
-		};
 /* End PBXShellScriptBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */

From c0e249badf57b2dd6f732e3033cb00e53fd34341 Mon Sep 17 00:00:00 2001
From: Rik Chilvers <rikchilvers@fastmail.com>
Date: Sat, 4 Nov 2017 11:02:00 +0000
Subject: [PATCH 28/48] prep for macOS example app

---
 .../AppDelegate.swift                         |   0
 .../Base.lproj/LaunchScreen.xib               |   0
 .../Base.lproj/Main.storyboard                |   0
 .../EventsTableViewController.swift           |   0
 .../Stuff.swift                               |   0
 .../StuffCollectionViewController.swift       |   0
 .../StuffTableViewController.swift            |   0
 .../AppIcon.appiconset/Contents.json          |  15 +
 .../LaunchImage.launchimage/Contents.json     |   9 +
 .../Supporting Files}/Info.plist              |   0
 .../DwifftExample.xcodeproj/project.pbxproj   | 248 +++++-
 .../DwifftExamples-macOS/AppDelegate.swift    |  20 +
 .../Base.lproj/Main.storyboard                | 717 ++++++++++++++++++
 .../AppIcon.appiconset/Contents.json          |  58 ++
 .../DwifftExamples_macOS.entitlements         |  10 +
 .../Supporting Files/Info.plist               |  32 +
 .../DwifftExamples-macOS/ViewController.swift |  21 +
 17 files changed, 1111 insertions(+), 19 deletions(-)
 rename DwifftExample/{DwifftExample => DwifftExample-iOS}/AppDelegate.swift (100%)
 rename DwifftExample/{DwifftExample => DwifftExample-iOS}/Base.lproj/LaunchScreen.xib (100%)
 rename DwifftExample/{DwifftExample => DwifftExample-iOS}/Base.lproj/Main.storyboard (100%)
 rename DwifftExample/{DwifftExample => DwifftExample-iOS}/EventsTableViewController.swift (100%)
 rename DwifftExample/{DwifftExample => DwifftExample-iOS}/Stuff.swift (100%)
 rename DwifftExample/{DwifftExample => DwifftExample-iOS}/StuffCollectionViewController.swift (100%)
 rename DwifftExample/{DwifftExample => DwifftExample-iOS}/StuffTableViewController.swift (100%)
 rename DwifftExample/{DwifftExample => DwifftExample-iOS/Supporting Files}/Images.xcassets/AppIcon.appiconset/Contents.json (68%)
 create mode 100644 DwifftExample/DwifftExample-iOS/Supporting Files/Images.xcassets/LaunchImage.launchimage/Contents.json
 rename DwifftExample/{DwifftExample => DwifftExample-iOS/Supporting Files}/Info.plist (100%)
 create mode 100644 DwifftExample/DwifftExamples-macOS/AppDelegate.swift
 create mode 100644 DwifftExample/DwifftExamples-macOS/Base.lproj/Main.storyboard
 create mode 100644 DwifftExample/DwifftExamples-macOS/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json
 create mode 100644 DwifftExample/DwifftExamples-macOS/Supporting Files/DwifftExamples_macOS.entitlements
 create mode 100644 DwifftExample/DwifftExamples-macOS/Supporting Files/Info.plist
 create mode 100644 DwifftExample/DwifftExamples-macOS/ViewController.swift

diff --git a/DwifftExample/DwifftExample/AppDelegate.swift b/DwifftExample/DwifftExample-iOS/AppDelegate.swift
similarity index 100%
rename from DwifftExample/DwifftExample/AppDelegate.swift
rename to DwifftExample/DwifftExample-iOS/AppDelegate.swift
diff --git a/DwifftExample/DwifftExample/Base.lproj/LaunchScreen.xib b/DwifftExample/DwifftExample-iOS/Base.lproj/LaunchScreen.xib
similarity index 100%
rename from DwifftExample/DwifftExample/Base.lproj/LaunchScreen.xib
rename to DwifftExample/DwifftExample-iOS/Base.lproj/LaunchScreen.xib
diff --git a/DwifftExample/DwifftExample/Base.lproj/Main.storyboard b/DwifftExample/DwifftExample-iOS/Base.lproj/Main.storyboard
similarity index 100%
rename from DwifftExample/DwifftExample/Base.lproj/Main.storyboard
rename to DwifftExample/DwifftExample-iOS/Base.lproj/Main.storyboard
diff --git a/DwifftExample/DwifftExample/EventsTableViewController.swift b/DwifftExample/DwifftExample-iOS/EventsTableViewController.swift
similarity index 100%
rename from DwifftExample/DwifftExample/EventsTableViewController.swift
rename to DwifftExample/DwifftExample-iOS/EventsTableViewController.swift
diff --git a/DwifftExample/DwifftExample/Stuff.swift b/DwifftExample/DwifftExample-iOS/Stuff.swift
similarity index 100%
rename from DwifftExample/DwifftExample/Stuff.swift
rename to DwifftExample/DwifftExample-iOS/Stuff.swift
diff --git a/DwifftExample/DwifftExample/StuffCollectionViewController.swift b/DwifftExample/DwifftExample-iOS/StuffCollectionViewController.swift
similarity index 100%
rename from DwifftExample/DwifftExample/StuffCollectionViewController.swift
rename to DwifftExample/DwifftExample-iOS/StuffCollectionViewController.swift
diff --git a/DwifftExample/DwifftExample/StuffTableViewController.swift b/DwifftExample/DwifftExample-iOS/StuffTableViewController.swift
similarity index 100%
rename from DwifftExample/DwifftExample/StuffTableViewController.swift
rename to DwifftExample/DwifftExample-iOS/StuffTableViewController.swift
diff --git a/DwifftExample/DwifftExample/Images.xcassets/AppIcon.appiconset/Contents.json b/DwifftExample/DwifftExample-iOS/Supporting Files/Images.xcassets/AppIcon.appiconset/Contents.json
similarity index 68%
rename from DwifftExample/DwifftExample/Images.xcassets/AppIcon.appiconset/Contents.json
rename to DwifftExample/DwifftExample-iOS/Supporting Files/Images.xcassets/AppIcon.appiconset/Contents.json
index 118c98f..19882d5 100644
--- a/DwifftExample/DwifftExample/Images.xcassets/AppIcon.appiconset/Contents.json
+++ b/DwifftExample/DwifftExample-iOS/Supporting Files/Images.xcassets/AppIcon.appiconset/Contents.json	
@@ -1,5 +1,15 @@
 {
   "images" : [
+    {
+      "idiom" : "iphone",
+      "size" : "20x20",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "20x20",
+      "scale" : "3x"
+    },
     {
       "idiom" : "iphone",
       "size" : "29x29",
@@ -29,6 +39,11 @@
       "idiom" : "iphone",
       "size" : "60x60",
       "scale" : "3x"
+    },
+    {
+      "idiom" : "ios-marketing",
+      "size" : "1024x1024",
+      "scale" : "1x"
     }
   ],
   "info" : {
diff --git a/DwifftExample/DwifftExample-iOS/Supporting Files/Images.xcassets/LaunchImage.launchimage/Contents.json b/DwifftExample/DwifftExample-iOS/Supporting Files/Images.xcassets/LaunchImage.launchimage/Contents.json
new file mode 100644
index 0000000..c5fda8e
--- /dev/null
+++ b/DwifftExample/DwifftExample-iOS/Supporting Files/Images.xcassets/LaunchImage.launchimage/Contents.json	
@@ -0,0 +1,9 @@
+{
+  "images" : [
+
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/DwifftExample/DwifftExample/Info.plist b/DwifftExample/DwifftExample-iOS/Supporting Files/Info.plist
similarity index 100%
rename from DwifftExample/DwifftExample/Info.plist
rename to DwifftExample/DwifftExample-iOS/Supporting Files/Info.plist
diff --git a/DwifftExample/DwifftExample.xcodeproj/project.pbxproj b/DwifftExample/DwifftExample.xcodeproj/project.pbxproj
index b96656e..f92eb73 100644
--- a/DwifftExample/DwifftExample.xcodeproj/project.pbxproj
+++ b/DwifftExample/DwifftExample.xcodeproj/project.pbxproj
@@ -17,6 +17,12 @@
 		0455022B1B898CF000F5614D /* StuffTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0455022A1B898CF000F5614D /* StuffTableViewController.swift */; };
 		046EA8A01E8CB1AA00FBD07D /* StuffCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 046EA89F1E8CB1AA00FBD07D /* StuffCollectionViewController.swift */; };
 		046EA8A31E8CBB9400FBD07D /* Stuff.swift in Sources */ = {isa = PBXBuildFile; fileRef = 046EA8A21E8CBB9400FBD07D /* Stuff.swift */; };
+		383AE64D1FADD1AA002B6725 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383AE64C1FADD1AA002B6725 /* AppDelegate.swift */; };
+		383AE64F1FADD1AA002B6725 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383AE64E1FADD1AA002B6725 /* ViewController.swift */; };
+		383AE6511FADD1AA002B6725 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 383AE6501FADD1AA002B6725 /* Assets.xcassets */; };
+		383AE6541FADD1AA002B6725 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 383AE6521FADD1AA002B6725 /* Main.storyboard */; };
+		3869E24B1FADD34400D85AA5 /* Dwifft.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 383AE65E1FADD1AA002B6725 /* Dwifft.framework */; };
+		3869E24C1FADD34400D85AA5 /* Dwifft.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 383AE65E1FADD1AA002B6725 /* Dwifft.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -48,6 +54,27 @@
 			remoteGlobalIDString = 041EBB911B89679200E113B3;
 			remoteInfo = Dwifft;
 		};
+		383AE65D1FADD1AA002B6725 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 045502181B898C5400F5614D /* Dwifft.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 382DEF421F487395007A8FD2;
+			remoteInfo = "Dwifft-macOS";
+		};
+		383AE65F1FADD1AA002B6725 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 045502181B898C5400F5614D /* Dwifft.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 38E5B5E21F7BDB5400F33285;
+			remoteInfo = "DwifftTests-macOS";
+		};
+		3869E24D1FADD34400D85AA5 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 045502181B898C5400F5614D /* Dwifft.xcodeproj */;
+			proxyType = 1;
+			remoteGlobalIDString = 382DEF411F487395007A8FD2;
+			remoteInfo = "Dwifft-macOS";
+		};
 		931446A91F3CA6FB00471406 /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			containerPortal = 045502181B898C5400F5614D /* Dwifft.xcodeproj */;
@@ -69,11 +96,22 @@
 			name = "Embed Frameworks";
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		3869E24F1FADD34400D85AA5 /* Embed Frameworks */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 10;
+			files = (
+				3869E24C1FADD34400D85AA5 /* Dwifft.framework in Embed Frameworks */,
+			);
+			name = "Embed Frameworks";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
 		0401A2081EA08151003C82E5 /* EventsTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventsTableViewController.swift; sourceTree = "<group>"; };
-		045501F31B898C3200F5614D /* DwifftExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DwifftExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		045501F31B898C3200F5614D /* DwifftExample-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DwifftExample-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
 		045501F71B898C3200F5614D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		045501F81B898C3200F5614D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
 		045501FD1B898C3200F5614D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
@@ -83,6 +121,13 @@
 		0455022A1B898CF000F5614D /* StuffTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StuffTableViewController.swift; sourceTree = "<group>"; };
 		046EA89F1E8CB1AA00FBD07D /* StuffCollectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StuffCollectionViewController.swift; sourceTree = "<group>"; };
 		046EA8A21E8CBB9400FBD07D /* Stuff.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stuff.swift; sourceTree = "<group>"; };
+		383AE64A1FADD1AA002B6725 /* DwifftExample-macOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DwifftExample-macOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+		383AE64C1FADD1AA002B6725 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+		383AE64E1FADD1AA002B6725 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
+		383AE6501FADD1AA002B6725 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+		383AE6531FADD1AA002B6725 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+		383AE6551FADD1AA002B6725 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		383AE6561FADD1AA002B6725 /* DwifftExamples_macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DwifftExamples_macOS.entitlements; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -94,6 +139,14 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		383AE6471FADD1AA002B6725 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				3869E24B1FADD34400D85AA5 /* Dwifft.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
@@ -101,7 +154,8 @@
 			isa = PBXGroup;
 			children = (
 				045502181B898C5400F5614D /* Dwifft.xcodeproj */,
-				045501F51B898C3200F5614D /* DwifftExample */,
+				045501F51B898C3200F5614D /* DwifftExample-iOS */,
+				383AE64B1FADD1AA002B6725 /* DwifftExamples-macOS */,
 				045501F41B898C3200F5614D /* Products */,
 			);
 			sourceTree = "<group>";
@@ -109,12 +163,13 @@
 		045501F41B898C3200F5614D /* Products */ = {
 			isa = PBXGroup;
 			children = (
-				045501F31B898C3200F5614D /* DwifftExample.app */,
+				045501F31B898C3200F5614D /* DwifftExample-iOS.app */,
+				383AE64A1FADD1AA002B6725 /* DwifftExample-macOS.app */,
 			);
 			name = Products;
 			sourceTree = "<group>";
 		};
-		045501F51B898C3200F5614D /* DwifftExample */ = {
+		045501F51B898C3200F5614D /* DwifftExample-iOS */ = {
 			isa = PBXGroup;
 			children = (
 				045501F81B898C3200F5614D /* AppDelegate.swift */,
@@ -123,19 +178,19 @@
 				0401A2081EA08151003C82E5 /* EventsTableViewController.swift */,
 				046EA8A21E8CBB9400FBD07D /* Stuff.swift */,
 				045501FC1B898C3200F5614D /* Main.storyboard */,
-				045501FF1B898C3200F5614D /* Images.xcassets */,
 				045502011B898C3200F5614D /* LaunchScreen.xib */,
 				045501F61B898C3200F5614D /* Supporting Files */,
 			);
-			path = DwifftExample;
+			path = "DwifftExample-iOS";
 			sourceTree = "<group>";
 		};
 		045501F61B898C3200F5614D /* Supporting Files */ = {
 			isa = PBXGroup;
 			children = (
+				045501FF1B898C3200F5614D /* Images.xcassets */,
 				045501F71B898C3200F5614D /* Info.plist */,
 			);
-			name = "Supporting Files";
+			path = "Supporting Files";
 			sourceTree = "<group>";
 		};
 		045502191B898C5400F5614D /* Products */ = {
@@ -144,16 +199,39 @@
 				0455021E1B898C5400F5614D /* Dwifft.framework */,
 				045502201B898C5400F5614D /* DwifftTests.xctest */,
 				931446AA1F3CA6FB00471406 /* Dwifft_tvOS.framework */,
+				383AE65E1FADD1AA002B6725 /* Dwifft.framework */,
+				383AE6601FADD1AA002B6725 /* DwifftTests-macOS.xctest */,
 			);
 			name = Products;
 			sourceTree = "<group>";
 		};
+		383AE64B1FADD1AA002B6725 /* DwifftExamples-macOS */ = {
+			isa = PBXGroup;
+			children = (
+				383AE6621FADD237002B6725 /* Supporting Files */,
+				383AE64C1FADD1AA002B6725 /* AppDelegate.swift */,
+				383AE64E1FADD1AA002B6725 /* ViewController.swift */,
+				383AE6521FADD1AA002B6725 /* Main.storyboard */,
+			);
+			path = "DwifftExamples-macOS";
+			sourceTree = "<group>";
+		};
+		383AE6621FADD237002B6725 /* Supporting Files */ = {
+			isa = PBXGroup;
+			children = (
+				383AE6551FADD1AA002B6725 /* Info.plist */,
+				383AE6561FADD1AA002B6725 /* DwifftExamples_macOS.entitlements */,
+				383AE6501FADD1AA002B6725 /* Assets.xcassets */,
+			);
+			path = "Supporting Files";
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
-		045501F21B898C3200F5614D /* DwifftExample */ = {
+		045501F21B898C3200F5614D /* DwifftExample-iOS */ = {
 			isa = PBXNativeTarget;
-			buildConfigurationList = 045502121B898C3200F5614D /* Build configuration list for PBXNativeTarget "DwifftExample" */;
+			buildConfigurationList = 045502121B898C3200F5614D /* Build configuration list for PBXNativeTarget "DwifftExample-iOS" */;
 			buildPhases = (
 				045501EF1B898C3200F5614D /* Sources */,
 				045501F01B898C3200F5614D /* Frameworks */,
@@ -166,9 +244,28 @@
 				045502221B898C5E00F5614D /* PBXTargetDependency */,
 				045502261B898C6600F5614D /* PBXTargetDependency */,
 			);
-			name = DwifftExample;
+			name = "DwifftExample-iOS";
 			productName = DwifftExample;
-			productReference = 045501F31B898C3200F5614D /* DwifftExample.app */;
+			productReference = 045501F31B898C3200F5614D /* DwifftExample-iOS.app */;
+			productType = "com.apple.product-type.application";
+		};
+		383AE6491FADD1AA002B6725 /* DwifftExample-macOS */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 383AE6611FADD1AA002B6725 /* Build configuration list for PBXNativeTarget "DwifftExample-macOS" */;
+			buildPhases = (
+				383AE6461FADD1AA002B6725 /* Sources */,
+				383AE6471FADD1AA002B6725 /* Frameworks */,
+				383AE6481FADD1AA002B6725 /* Resources */,
+				3869E24F1FADD34400D85AA5 /* Embed Frameworks */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				3869E24E1FADD34400D85AA5 /* PBXTargetDependency */,
+			);
+			name = "DwifftExample-macOS";
+			productName = "DwifftExamples-macOS";
+			productReference = 383AE64A1FADD1AA002B6725 /* DwifftExample-macOS.app */;
 			productType = "com.apple.product-type.application";
 		};
 /* End PBXNativeTarget section */
@@ -177,13 +274,17 @@
 		045501EB1B898C3200F5614D /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastSwiftUpdateCheck = 0700;
+				LastSwiftUpdateCheck = 0910;
 				LastUpgradeCheck = 0900;
 				ORGANIZATIONNAME = jflinter;
 				TargetAttributes = {
 					045501F21B898C3200F5614D = {
 						CreatedOnToolsVersion = 6.4;
-						LastSwiftMigration = 0800;
+						LastSwiftMigration = 0910;
+					};
+					383AE6491FADD1AA002B6725 = {
+						CreatedOnToolsVersion = 9.1;
+						ProvisioningStyle = Automatic;
 					};
 				};
 			};
@@ -206,7 +307,8 @@
 			);
 			projectRoot = "";
 			targets = (
-				045501F21B898C3200F5614D /* DwifftExample */,
+				045501F21B898C3200F5614D /* DwifftExample-iOS */,
+				383AE6491FADD1AA002B6725 /* DwifftExample-macOS */,
 			);
 		};
 /* End PBXProject section */
@@ -226,6 +328,20 @@
 			remoteRef = 0455021F1B898C5400F5614D /* PBXContainerItemProxy */;
 			sourceTree = BUILT_PRODUCTS_DIR;
 		};
+		383AE65E1FADD1AA002B6725 /* Dwifft.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = Dwifft.framework;
+			remoteRef = 383AE65D1FADD1AA002B6725 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		383AE6601FADD1AA002B6725 /* DwifftTests-macOS.xctest */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.cfbundle;
+			path = "DwifftTests-macOS.xctest";
+			remoteRef = 383AE65F1FADD1AA002B6725 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
 		931446AA1F3CA6FB00471406 /* Dwifft_tvOS.framework */ = {
 			isa = PBXReferenceProxy;
 			fileType = wrapper.framework;
@@ -246,6 +362,15 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		383AE6481FADD1AA002B6725 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				383AE6511FADD1AA002B6725 /* Assets.xcassets in Resources */,
+				383AE6541FADD1AA002B6725 /* Main.storyboard in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
@@ -261,6 +386,15 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		383AE6461FADD1AA002B6725 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				383AE64F1FADD1AA002B6725 /* ViewController.swift in Sources */,
+				383AE64D1FADD1AA002B6725 /* AppDelegate.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXTargetDependency section */
@@ -274,6 +408,11 @@
 			name = Dwifft;
 			targetProxy = 045502251B898C6600F5614D /* PBXContainerItemProxy */;
 		};
+		3869E24E1FADD34400D85AA5 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			name = "Dwifft-macOS";
+			targetProxy = 3869E24D1FADD34400D85AA5 /* PBXContainerItemProxy */;
+		};
 /* End PBXTargetDependency section */
 
 /* Begin PBXVariantGroup section */
@@ -293,6 +432,14 @@
 			name = LaunchScreen.xib;
 			sourceTree = "<group>";
 		};
+		383AE6521FADD1AA002B6725 /* Main.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				383AE6531FADD1AA002B6725 /* Base */,
+			);
+			name = Main.storyboard;
+			sourceTree = "<group>";
+		};
 /* End PBXVariantGroup section */
 
 /* Begin XCBuildConfiguration section */
@@ -403,12 +550,13 @@
 				IBC_WARNINGS = NO;
 				IBSC_NOTICES = NO;
 				IBSC_WARNINGS = NO;
-				INFOPLIST_FILE = DwifftExample/Info.plist;
+				INFOPLIST_FILE = "DwifftExample-iOS/Supporting Files/Info.plist";
 				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SWIFT_VERSION = 3.0;
+				SWIFT_SWIFT3_OBJC_INFERENCE = Default;
+				SWIFT_VERSION = 4.0;
 			};
 			name = Debug;
 		};
@@ -420,12 +568,65 @@
 				IBC_WARNINGS = NO;
 				IBSC_NOTICES = NO;
 				IBSC_WARNINGS = NO;
-				INFOPLIST_FILE = DwifftExample/Info.plist;
+				INFOPLIST_FILE = "DwifftExample-iOS/Supporting Files/Info.plist";
 				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SWIFT_VERSION = 3.0;
+				SWIFT_SWIFT3_OBJC_INFERENCE = Default;
+				SWIFT_VERSION = 4.0;
+			};
+			name = Release;
+		};
+		383AE6571FADD1AA002B6725 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_ENTITLEMENTS = "DwifftExamples-macOS/Supporting Files/DwifftExamples_macOS.entitlements";
+				CODE_SIGN_IDENTITY = "-";
+				CODE_SIGN_STYLE = Automatic;
+				COMBINE_HIDPI_IMAGES = YES;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				INFOPLIST_FILE = "DwifftExamples-macOS/Supporting Files/Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.11;
+				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.DwifftExamples-macOS";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = macosx;
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+				SWIFT_VERSION = 4.0;
+			};
+			name = Debug;
+		};
+		383AE6581FADD1AA002B6725 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_ENTITLEMENTS = "DwifftExamples-macOS/Supporting Files/DwifftExamples_macOS.entitlements";
+				CODE_SIGN_IDENTITY = "-";
+				CODE_SIGN_STYLE = Automatic;
+				COMBINE_HIDPI_IMAGES = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				INFOPLIST_FILE = "DwifftExamples-macOS/Supporting Files/Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.11;
+				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.DwifftExamples-macOS";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = macosx;
+				SWIFT_VERSION = 4.0;
 			};
 			name = Release;
 		};
@@ -441,7 +642,7 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		045502121B898C3200F5614D /* Build configuration list for PBXNativeTarget "DwifftExample" */ = {
+		045502121B898C3200F5614D /* Build configuration list for PBXNativeTarget "DwifftExample-iOS" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				045502131B898C3200F5614D /* Debug */,
@@ -450,6 +651,15 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
+		383AE6611FADD1AA002B6725 /* Build configuration list for PBXNativeTarget "DwifftExample-macOS" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				383AE6571FADD1AA002B6725 /* Debug */,
+				383AE6581FADD1AA002B6725 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 /* End XCConfigurationList section */
 	};
 	rootObject = 045501EB1B898C3200F5614D /* Project object */;
diff --git a/DwifftExample/DwifftExamples-macOS/AppDelegate.swift b/DwifftExample/DwifftExamples-macOS/AppDelegate.swift
new file mode 100644
index 0000000..1147aa7
--- /dev/null
+++ b/DwifftExample/DwifftExamples-macOS/AppDelegate.swift
@@ -0,0 +1,20 @@
+//
+
+import Cocoa
+
+@NSApplicationMain
+class AppDelegate: NSObject, NSApplicationDelegate {
+
+
+
+  func applicationDidFinishLaunching(_ aNotification: Notification) {
+    // Insert code here to initialize your application
+  }
+
+  func applicationWillTerminate(_ aNotification: Notification) {
+    // Insert code here to tear down your application
+  }
+
+
+}
+
diff --git a/DwifftExample/DwifftExamples-macOS/Base.lproj/Main.storyboard b/DwifftExample/DwifftExamples-macOS/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..786bd90
--- /dev/null
+++ b/DwifftExample/DwifftExamples-macOS/Base.lproj/Main.storyboard
@@ -0,0 +1,717 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11134"/>
+    </dependencies>
+    <scenes>
+        <!--Application-->
+        <scene sceneID="JPo-4y-FX3">
+            <objects>
+                <application id="hnw-xV-0zn" sceneMemberID="viewController">
+                    <menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
+                        <items>
+                            <menuItem title="DwifftExamples-macOS" id="1Xt-HY-uBw">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="DwifftExamples-macOS" systemMenu="apple" id="uQy-DD-JDr">
+                                    <items>
+                                        <menuItem title="About DwifftExamples-macOS" id="5kV-Vb-QxS">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="orderFrontStandardAboutPanel:" target="Ady-hI-5gd" id="Exp-CZ-Vem"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
+                                        <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
+                                        <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
+                                        <menuItem title="Services" id="NMo-om-nkz">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
+                                        <menuItem title="Hide DwifftExamples-macOS" keyEquivalent="h" id="Olw-nP-bQN">
+                                            <connections>
+                                                <action selector="hide:" target="Ady-hI-5gd" id="PnN-Uc-m68"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
+                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="hideOtherApplications:" target="Ady-hI-5gd" id="VT4-aY-XCT"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Show All" id="Kd2-mp-pUS">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="unhideAllApplications:" target="Ady-hI-5gd" id="Dhg-Le-xox"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
+                                        <menuItem title="Quit DwifftExamples-macOS" keyEquivalent="q" id="4sb-4s-VLi">
+                                            <connections>
+                                                <action selector="terminate:" target="Ady-hI-5gd" id="Te7-pn-YzF"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="File" id="dMs-cI-mzQ">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="File" id="bib-Uj-vzu">
+                                    <items>
+                                        <menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
+                                            <connections>
+                                                <action selector="newDocument:" target="Ady-hI-5gd" id="4Si-XN-c54"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
+                                            <connections>
+                                                <action selector="openDocument:" target="Ady-hI-5gd" id="bVn-NM-KNZ"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Open Recent" id="tXI-mr-wws">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
+                                                <items>
+                                                    <menuItem title="Clear Menu" id="vNY-rz-j42">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="clearRecentDocuments:" target="Ady-hI-5gd" id="Daa-9d-B3U"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                </items>
+                                            </menu>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
+                                        <menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
+                                            <connections>
+                                                <action selector="performClose:" target="Ady-hI-5gd" id="HmO-Ls-i7Q"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
+                                            <connections>
+                                                <action selector="saveDocument:" target="Ady-hI-5gd" id="teZ-XB-qJY"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
+                                            <connections>
+                                                <action selector="saveDocumentAs:" target="Ady-hI-5gd" id="mDf-zr-I0C"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Revert to Saved" keyEquivalent="r" id="KaW-ft-85H">
+                                            <connections>
+                                                <action selector="revertDocumentToSaved:" target="Ady-hI-5gd" id="iJ3-Pv-kwq"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
+                                        <menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
+                                            <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="runPageLayout:" target="Ady-hI-5gd" id="Din-rz-gC5"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
+                                            <connections>
+                                                <action selector="print:" target="Ady-hI-5gd" id="qaZ-4w-aoO"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Edit" id="5QF-Oa-p0T">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Edit" id="W48-6f-4Dl">
+                                    <items>
+                                        <menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
+                                            <connections>
+                                                <action selector="undo:" target="Ady-hI-5gd" id="M6e-cu-g7V"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
+                                            <connections>
+                                                <action selector="redo:" target="Ady-hI-5gd" id="oIA-Rs-6OD"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
+                                        <menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
+                                            <connections>
+                                                <action selector="cut:" target="Ady-hI-5gd" id="YJe-68-I9s"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
+                                            <connections>
+                                                <action selector="copy:" target="Ady-hI-5gd" id="G1f-GL-Joy"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
+                                            <connections>
+                                                <action selector="paste:" target="Ady-hI-5gd" id="UvS-8e-Qdg"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
+                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="pasteAsPlainText:" target="Ady-hI-5gd" id="cEh-KX-wJQ"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Delete" id="pa3-QI-u2k">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="delete:" target="Ady-hI-5gd" id="0Mk-Ml-PaM"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
+                                            <connections>
+                                                <action selector="selectAll:" target="Ady-hI-5gd" id="VNm-Mi-diN"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
+                                        <menuItem title="Find" id="4EN-yA-p0u">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <menu key="submenu" title="Find" id="1b7-l0-nxx">
+                                                <items>
+                                                    <menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
+                                                        <connections>
+                                                            <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="cD7-Qs-BN4"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
+                                                        <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                                        <connections>
+                                                            <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="WD3-Gg-5AJ"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
+                                                        <connections>
+                                                            <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="NDo-RZ-v9R"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
+                                                        <connections>
+                                                            <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="HOh-sY-3ay"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
+                                                        <connections>
+                                                            <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="U76-nv-p5D"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
+                                                        <connections>
+                                                            <action selector="centerSelectionInVisibleArea:" target="Ady-hI-5gd" id="IOG-6D-g5B"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                </items>
+                                            </menu>
+                                        </menuItem>
+                                        <menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
+                                                <items>
+                                                    <menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
+                                                        <connections>
+                                                            <action selector="showGuessPanel:" target="Ady-hI-5gd" id="vFj-Ks-hy3"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
+                                                        <connections>
+                                                            <action selector="checkSpelling:" target="Ady-hI-5gd" id="fz7-VC-reM"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
+                                                    <menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="toggleContinuousSpellChecking:" target="Ady-hI-5gd" id="7w6-Qz-0kB"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="toggleGrammarChecking:" target="Ady-hI-5gd" id="muD-Qn-j4w"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="toggleAutomaticSpellingCorrection:" target="Ady-hI-5gd" id="2lM-Qi-WAP"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                </items>
+                                            </menu>
+                                        </menuItem>
+                                        <menuItem title="Substitutions" id="9ic-FL-obx">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
+                                                <items>
+                                                    <menuItem title="Show Substitutions" id="z6F-FW-3nz">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="orderFrontSubstitutionsPanel:" target="Ady-hI-5gd" id="oku-mr-iSq"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
+                                                    <menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="toggleSmartInsertDelete:" target="Ady-hI-5gd" id="3IJ-Se-DZD"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Smart Quotes" id="hQb-2v-fYv">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="toggleAutomaticQuoteSubstitution:" target="Ady-hI-5gd" id="ptq-xd-QOA"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Smart Dashes" id="rgM-f4-ycn">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="toggleAutomaticDashSubstitution:" target="Ady-hI-5gd" id="oCt-pO-9gS"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Smart Links" id="cwL-P1-jid">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="toggleAutomaticLinkDetection:" target="Ady-hI-5gd" id="Gip-E3-Fov"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Data Detectors" id="tRr-pd-1PS">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="toggleAutomaticDataDetection:" target="Ady-hI-5gd" id="R1I-Nq-Kbl"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Text Replacement" id="HFQ-gK-NFA">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="toggleAutomaticTextReplacement:" target="Ady-hI-5gd" id="DvP-Fe-Py6"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                </items>
+                                            </menu>
+                                        </menuItem>
+                                        <menuItem title="Transformations" id="2oI-Rn-ZJC">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <menu key="submenu" title="Transformations" id="c8a-y6-VQd">
+                                                <items>
+                                                    <menuItem title="Make Upper Case" id="vmV-6d-7jI">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="uppercaseWord:" target="Ady-hI-5gd" id="sPh-Tk-edu"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Make Lower Case" id="d9M-CD-aMd">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="lowercaseWord:" target="Ady-hI-5gd" id="iUZ-b5-hil"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Capitalize" id="UEZ-Bs-lqG">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="capitalizeWord:" target="Ady-hI-5gd" id="26H-TL-nsh"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                </items>
+                                            </menu>
+                                        </menuItem>
+                                        <menuItem title="Speech" id="xrE-MZ-jX0">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <menu key="submenu" title="Speech" id="3rS-ZA-NoH">
+                                                <items>
+                                                    <menuItem title="Start Speaking" id="Ynk-f8-cLZ">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="startSpeaking:" target="Ady-hI-5gd" id="654-Ng-kyl"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Stop Speaking" id="Oyz-dy-DGm">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="stopSpeaking:" target="Ady-hI-5gd" id="dX8-6p-jy9"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                </items>
+                                            </menu>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Format" id="jxT-CU-nIS">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Format" id="GEO-Iw-cKr">
+                                    <items>
+                                        <menuItem title="Font" id="Gi5-1S-RQB">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
+                                                <items>
+                                                    <menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
+                                                        <connections>
+                                                            <action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
+                                                        <connections>
+                                                            <action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
+                                                        <connections>
+                                                            <action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
+                                                        <connections>
+                                                            <action selector="underline:" target="Ady-hI-5gd" id="FYS-2b-JAY"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
+                                                    <menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
+                                                        <connections>
+                                                            <action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
+                                                        <connections>
+                                                            <action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
+                                                    <menuItem title="Kern" id="jBQ-r6-VK2">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <menu key="submenu" title="Kern" id="tlD-Oa-oAM">
+                                                            <items>
+                                                                <menuItem title="Use Default" id="GUa-eO-cwY">
+                                                                    <modifierMask key="keyEquivalentModifierMask"/>
+                                                                    <connections>
+                                                                        <action selector="useStandardKerning:" target="Ady-hI-5gd" id="6dk-9l-Ckg"/>
+                                                                    </connections>
+                                                                </menuItem>
+                                                                <menuItem title="Use None" id="cDB-IK-hbR">
+                                                                    <modifierMask key="keyEquivalentModifierMask"/>
+                                                                    <connections>
+                                                                        <action selector="turnOffKerning:" target="Ady-hI-5gd" id="U8a-gz-Maa"/>
+                                                                    </connections>
+                                                                </menuItem>
+                                                                <menuItem title="Tighten" id="46P-cB-AYj">
+                                                                    <modifierMask key="keyEquivalentModifierMask"/>
+                                                                    <connections>
+                                                                        <action selector="tightenKerning:" target="Ady-hI-5gd" id="hr7-Nz-8ro"/>
+                                                                    </connections>
+                                                                </menuItem>
+                                                                <menuItem title="Loosen" id="ogc-rX-tC1">
+                                                                    <modifierMask key="keyEquivalentModifierMask"/>
+                                                                    <connections>
+                                                                        <action selector="loosenKerning:" target="Ady-hI-5gd" id="8i4-f9-FKE"/>
+                                                                    </connections>
+                                                                </menuItem>
+                                                            </items>
+                                                        </menu>
+                                                    </menuItem>
+                                                    <menuItem title="Ligatures" id="o6e-r0-MWq">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
+                                                            <items>
+                                                                <menuItem title="Use Default" id="agt-UL-0e3">
+                                                                    <modifierMask key="keyEquivalentModifierMask"/>
+                                                                    <connections>
+                                                                        <action selector="useStandardLigatures:" target="Ady-hI-5gd" id="7uR-wd-Dx6"/>
+                                                                    </connections>
+                                                                </menuItem>
+                                                                <menuItem title="Use None" id="J7y-lM-qPV">
+                                                                    <modifierMask key="keyEquivalentModifierMask"/>
+                                                                    <connections>
+                                                                        <action selector="turnOffLigatures:" target="Ady-hI-5gd" id="iX2-gA-Ilz"/>
+                                                                    </connections>
+                                                                </menuItem>
+                                                                <menuItem title="Use All" id="xQD-1f-W4t">
+                                                                    <modifierMask key="keyEquivalentModifierMask"/>
+                                                                    <connections>
+                                                                        <action selector="useAllLigatures:" target="Ady-hI-5gd" id="KcB-kA-TuK"/>
+                                                                    </connections>
+                                                                </menuItem>
+                                                            </items>
+                                                        </menu>
+                                                    </menuItem>
+                                                    <menuItem title="Baseline" id="OaQ-X3-Vso">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <menu key="submenu" title="Baseline" id="ijk-EB-dga">
+                                                            <items>
+                                                                <menuItem title="Use Default" id="3Om-Ey-2VK">
+                                                                    <modifierMask key="keyEquivalentModifierMask"/>
+                                                                    <connections>
+                                                                        <action selector="unscript:" target="Ady-hI-5gd" id="0vZ-95-Ywn"/>
+                                                                    </connections>
+                                                                </menuItem>
+                                                                <menuItem title="Superscript" id="Rqc-34-cIF">
+                                                                    <modifierMask key="keyEquivalentModifierMask"/>
+                                                                    <connections>
+                                                                        <action selector="superscript:" target="Ady-hI-5gd" id="3qV-fo-wpU"/>
+                                                                    </connections>
+                                                                </menuItem>
+                                                                <menuItem title="Subscript" id="I0S-gh-46l">
+                                                                    <modifierMask key="keyEquivalentModifierMask"/>
+                                                                    <connections>
+                                                                        <action selector="subscript:" target="Ady-hI-5gd" id="Q6W-4W-IGz"/>
+                                                                    </connections>
+                                                                </menuItem>
+                                                                <menuItem title="Raise" id="2h7-ER-AoG">
+                                                                    <modifierMask key="keyEquivalentModifierMask"/>
+                                                                    <connections>
+                                                                        <action selector="raiseBaseline:" target="Ady-hI-5gd" id="4sk-31-7Q9"/>
+                                                                    </connections>
+                                                                </menuItem>
+                                                                <menuItem title="Lower" id="1tx-W0-xDw">
+                                                                    <modifierMask key="keyEquivalentModifierMask"/>
+                                                                    <connections>
+                                                                        <action selector="lowerBaseline:" target="Ady-hI-5gd" id="OF1-bc-KW4"/>
+                                                                    </connections>
+                                                                </menuItem>
+                                                            </items>
+                                                        </menu>
+                                                    </menuItem>
+                                                    <menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
+                                                    <menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
+                                                        <connections>
+                                                            <action selector="orderFrontColorPanel:" target="Ady-hI-5gd" id="mSX-Xz-DV3"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
+                                                    <menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
+                                                        <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                                        <connections>
+                                                            <action selector="copyFont:" target="Ady-hI-5gd" id="GJO-xA-L4q"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
+                                                        <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                                        <connections>
+                                                            <action selector="pasteFont:" target="Ady-hI-5gd" id="JfD-CL-leO"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                </items>
+                                            </menu>
+                                        </menuItem>
+                                        <menuItem title="Text" id="Fal-I4-PZk">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <menu key="submenu" title="Text" id="d9c-me-L2H">
+                                                <items>
+                                                    <menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
+                                                        <connections>
+                                                            <action selector="alignLeft:" target="Ady-hI-5gd" id="zUv-R1-uAa"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
+                                                        <connections>
+                                                            <action selector="alignCenter:" target="Ady-hI-5gd" id="spX-mk-kcS"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Justify" id="J5U-5w-g23">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="alignJustified:" target="Ady-hI-5gd" id="ljL-7U-jND"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
+                                                        <connections>
+                                                            <action selector="alignRight:" target="Ady-hI-5gd" id="r48-bG-YeY"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
+                                                    <menuItem title="Writing Direction" id="H1b-Si-o9J">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
+                                                            <items>
+                                                                <menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
+                                                                    <modifierMask key="keyEquivalentModifierMask"/>
+                                                                </menuItem>
+                                                                <menuItem id="YGs-j5-SAR">
+                                                                    <string key="title">	Default</string>
+                                                                    <modifierMask key="keyEquivalentModifierMask"/>
+                                                                    <connections>
+                                                                        <action selector="makeBaseWritingDirectionNatural:" target="Ady-hI-5gd" id="qtV-5e-UBP"/>
+                                                                    </connections>
+                                                                </menuItem>
+                                                                <menuItem id="Lbh-J2-qVU">
+                                                                    <string key="title">	Left to Right</string>
+                                                                    <modifierMask key="keyEquivalentModifierMask"/>
+                                                                    <connections>
+                                                                        <action selector="makeBaseWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="S0X-9S-QSf"/>
+                                                                    </connections>
+                                                                </menuItem>
+                                                                <menuItem id="jFq-tB-4Kx">
+                                                                    <string key="title">	Right to Left</string>
+                                                                    <modifierMask key="keyEquivalentModifierMask"/>
+                                                                    <connections>
+                                                                        <action selector="makeBaseWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="5fk-qB-AqJ"/>
+                                                                    </connections>
+                                                                </menuItem>
+                                                                <menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
+                                                                <menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
+                                                                    <modifierMask key="keyEquivalentModifierMask"/>
+                                                                </menuItem>
+                                                                <menuItem id="Nop-cj-93Q">
+                                                                    <string key="title">	Default</string>
+                                                                    <modifierMask key="keyEquivalentModifierMask"/>
+                                                                    <connections>
+                                                                        <action selector="makeTextWritingDirectionNatural:" target="Ady-hI-5gd" id="lPI-Se-ZHp"/>
+                                                                    </connections>
+                                                                </menuItem>
+                                                                <menuItem id="BgM-ve-c93">
+                                                                    <string key="title">	Left to Right</string>
+                                                                    <modifierMask key="keyEquivalentModifierMask"/>
+                                                                    <connections>
+                                                                        <action selector="makeTextWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="caW-Bv-w94"/>
+                                                                    </connections>
+                                                                </menuItem>
+                                                                <menuItem id="RB4-Sm-HuC">
+                                                                    <string key="title">	Right to Left</string>
+                                                                    <modifierMask key="keyEquivalentModifierMask"/>
+                                                                    <connections>
+                                                                        <action selector="makeTextWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="EXD-6r-ZUu"/>
+                                                                    </connections>
+                                                                </menuItem>
+                                                            </items>
+                                                        </menu>
+                                                    </menuItem>
+                                                    <menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
+                                                    <menuItem title="Show Ruler" id="vLm-3I-IUL">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="toggleRuler:" target="Ady-hI-5gd" id="FOx-HJ-KwY"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
+                                                        <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+                                                        <connections>
+                                                            <action selector="copyRuler:" target="Ady-hI-5gd" id="71i-fW-3W2"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
+                                                        <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+                                                        <connections>
+                                                            <action selector="pasteRuler:" target="Ady-hI-5gd" id="cSh-wd-qM2"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                </items>
+                                            </menu>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="View" id="H8h-7b-M4v">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="View" id="HyV-fh-RgO">
+                                    <items>
+                                        <menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
+                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="toggleToolbarShown:" target="Ady-hI-5gd" id="BXY-wc-z0C"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="runToolbarCustomizationPalette:" target="Ady-hI-5gd" id="pQI-g3-MTW"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="hB3-LF-h0Y"/>
+                                        <menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE">
+                                            <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="toggleSourceList:" target="Ady-hI-5gd" id="iwa-gc-5KM"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
+                                            <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="toggleFullScreen:" target="Ady-hI-5gd" id="dU3-MA-1Rq"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Window" id="aUF-d1-5bR">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
+                                    <items>
+                                        <menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
+                                            <connections>
+                                                <action selector="performMiniaturize:" target="Ady-hI-5gd" id="VwT-WD-YPe"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Zoom" id="R4o-n2-Eq4">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="performZoom:" target="Ady-hI-5gd" id="DIl-cC-cCs"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
+                                        <menuItem title="Bring All to Front" id="LE2-aR-0XJ">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="arrangeInFront:" target="Ady-hI-5gd" id="DRN-fu-gQh"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Help" id="wpr-3q-Mcd">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
+                                    <items>
+                                        <menuItem title="DwifftExamples-macOS Help" keyEquivalent="?" id="FKE-Sm-Kum">
+                                            <connections>
+                                                <action selector="showHelp:" target="Ady-hI-5gd" id="y7X-2Q-9no"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                        </items>
+                    </menu>
+                    <connections>
+                        <outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
+                    </connections>
+                </application>
+                <customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModuleProvider="target"/>
+                <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
+                <customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="75" y="0.0"/>
+        </scene>
+        <!--Window Controller-->
+        <scene sceneID="R2V-B0-nI4">
+            <objects>
+                <windowController id="B8D-0N-5wS" sceneMemberID="viewController">
+                    <window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="IQv-IB-iLA">
+                        <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
+                        <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
+                        <rect key="contentRect" x="196" y="240" width="480" height="270"/>
+                        <rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
+			<connections>
+                            <outlet property="delegate" destination="B8D-0N-5wS" id="98r-iN-zZc"/>
+                        </connections>
+                    </window>
+                    <connections>
+                        <segue destination="XfG-lQ-9wD" kind="relationship" relationship="window.shadowedContentViewController" id="cq2-FE-JQM"/>
+                    </connections>
+                </windowController>
+                <customObject id="Oky-zY-oP4" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="75" y="250"/>
+        </scene>
+        <!--View Controller-->
+        <scene sceneID="hIz-AP-VOD">
+            <objects>
+                <viewController id="XfG-lQ-9wD" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" wantsLayer="YES" id="m2S-Jp-Qdl">
+                        <rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
+                        <autoresizingMask key="autoresizingMask"/>
+                    </view>
+                </viewController>
+                <customObject id="rPt-NT-nkU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="75" y="655"/>
+        </scene>
+    </scenes>
+</document>
diff --git a/DwifftExample/DwifftExamples-macOS/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json b/DwifftExample/DwifftExamples-macOS/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..2db2b1c
--- /dev/null
+++ b/DwifftExample/DwifftExamples-macOS/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json	
@@ -0,0 +1,58 @@
+{
+  "images" : [
+    {
+      "idiom" : "mac",
+      "size" : "16x16",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "16x16",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "32x32",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "32x32",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "128x128",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "128x128",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "256x256",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "256x256",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "512x512",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "512x512",
+      "scale" : "2x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/DwifftExample/DwifftExamples-macOS/Supporting Files/DwifftExamples_macOS.entitlements b/DwifftExample/DwifftExamples-macOS/Supporting Files/DwifftExamples_macOS.entitlements
new file mode 100644
index 0000000..f2ef3ae
--- /dev/null
+++ b/DwifftExample/DwifftExamples-macOS/Supporting Files/DwifftExamples_macOS.entitlements	
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+    <key>com.apple.security.app-sandbox</key>
+    <true/>
+    <key>com.apple.security.files.user-selected.read-only</key>
+    <true/>
+</dict>
+</plist>
diff --git a/DwifftExample/DwifftExamples-macOS/Supporting Files/Info.plist b/DwifftExample/DwifftExamples-macOS/Supporting Files/Info.plist
new file mode 100644
index 0000000..44e2f49
--- /dev/null
+++ b/DwifftExample/DwifftExamples-macOS/Supporting Files/Info.plist	
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIconFile</key>
+	<string></string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSMinimumSystemVersion</key>
+	<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
+	<key>NSHumanReadableCopyright</key>
+	<string>Copyright © 2017 jflinter. All rights reserved.</string>
+	<key>NSMainStoryboardFile</key>
+	<string>Main</string>
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+</dict>
+</plist>
diff --git a/DwifftExample/DwifftExamples-macOS/ViewController.swift b/DwifftExample/DwifftExamples-macOS/ViewController.swift
new file mode 100644
index 0000000..b99a9d0
--- /dev/null
+++ b/DwifftExample/DwifftExamples-macOS/ViewController.swift
@@ -0,0 +1,21 @@
+//
+
+import Cocoa
+
+class ViewController: NSViewController {
+
+  override func viewDidLoad() {
+    super.viewDidLoad()
+
+    // Do any additional setup after loading the view.
+  }
+
+  override var representedObject: Any? {
+    didSet {
+    // Update the view, if already loaded.
+    }
+  }
+
+
+}
+

From 7c8b0aa7250f0a91c84f5f768617445c203be650 Mon Sep 17 00:00:00 2001
From: Rik Chilvers <rikchilvers@fastmail.com>
Date: Sun, 5 Nov 2017 13:24:13 +0000
Subject: [PATCH 29/48] adjust section index

when inserting beyond collectionView.numberOfSections
---
 Dwifft/Dwifft+AppKit.swift | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/Dwifft/Dwifft+AppKit.swift b/Dwifft/Dwifft+AppKit.swift
index 21d4e34..27f76c5 100644
--- a/Dwifft/Dwifft+AppKit.swift
+++ b/Dwifft/Dwifft+AppKit.swift
@@ -146,7 +146,14 @@ public final class CollectionViewDiffCalculator<Section: Equatable, Value: Equat
                 case let .delete(section, item, _): collectionView.deleteItems(at: [IndexPath(item: item, section: section)])
                 case let .insert(section, item, _): collectionView.insertItems(at: [IndexPath(item: item, section: section)])
                 case let .sectionDelete(section, _): collectionView.deleteSections(IndexSet(integer: section))
-                case let .sectionInsert(section, _): collectionView.insertSections(IndexSet(integer: section))
+                case let .sectionInsert(section, _):
+                  // NSCollectionViews don't seem to like it when inserting sections beyond numberOfSections
+                  // so adjust for that
+                  if section > collectionView.numberOfSections {
+                    collectionView.insertSections(IndexSet(integer: collectionView.numberOfSections))
+                  } else {
+                    collectionView.insertSections(IndexSet(integer: section))
+                  }
                 }
             }
         }, completionHandler: nil)

From 784f7de213f6d945743db7bb5548e04118dd65ca Mon Sep 17 00:00:00 2001
From: Rik Chilvers <rikchilvers@fastmail.com>
Date: Sun, 5 Nov 2017 13:24:57 +0000
Subject: [PATCH 30/48] macOS example

---
 .../DwifftExample.xcodeproj/project.pbxproj   |  36 ++-
 .../Base.lproj/Main.storyboard                | 205 ++++++++++++++++--
 .../StuffCollectionHeaderView.xib             |  39 ++++
 .../StuffCollectionViewController.swift       |  69 ++++++
 .../StuffCollectionViewItem.swift             |   3 +
 .../StuffCollectionViewItem.xib               |  39 ++++
 .../StuffTableViewController.swift            |  46 ++++
 .../DwifftExamples-macOS/ViewController.swift |  21 --
 .../{DwifftExample-iOS => Shared}/Stuff.swift |   6 +
 9 files changed, 423 insertions(+), 41 deletions(-)
 create mode 100644 DwifftExample/DwifftExamples-macOS/StuffCollectionHeaderView.xib
 create mode 100644 DwifftExample/DwifftExamples-macOS/StuffCollectionViewController.swift
 create mode 100644 DwifftExample/DwifftExamples-macOS/StuffCollectionViewItem.swift
 create mode 100644 DwifftExample/DwifftExamples-macOS/StuffCollectionViewItem.xib
 create mode 100644 DwifftExample/DwifftExamples-macOS/StuffTableViewController.swift
 delete mode 100644 DwifftExample/DwifftExamples-macOS/ViewController.swift
 rename DwifftExample/{DwifftExample-iOS => Shared}/Stuff.swift (93%)

diff --git a/DwifftExample/DwifftExample.xcodeproj/project.pbxproj b/DwifftExample/DwifftExample.xcodeproj/project.pbxproj
index f92eb73..a1c0d9f 100644
--- a/DwifftExample/DwifftExample.xcodeproj/project.pbxproj
+++ b/DwifftExample/DwifftExample.xcodeproj/project.pbxproj
@@ -18,11 +18,16 @@
 		046EA8A01E8CB1AA00FBD07D /* StuffCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 046EA89F1E8CB1AA00FBD07D /* StuffCollectionViewController.swift */; };
 		046EA8A31E8CBB9400FBD07D /* Stuff.swift in Sources */ = {isa = PBXBuildFile; fileRef = 046EA8A21E8CBB9400FBD07D /* Stuff.swift */; };
 		383AE64D1FADD1AA002B6725 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383AE64C1FADD1AA002B6725 /* AppDelegate.swift */; };
-		383AE64F1FADD1AA002B6725 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383AE64E1FADD1AA002B6725 /* ViewController.swift */; };
+		383AE64F1FADD1AA002B6725 /* StuffTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383AE64E1FADD1AA002B6725 /* StuffTableViewController.swift */; };
 		383AE6511FADD1AA002B6725 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 383AE6501FADD1AA002B6725 /* Assets.xcassets */; };
 		383AE6541FADD1AA002B6725 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 383AE6521FADD1AA002B6725 /* Main.storyboard */; };
 		3869E24B1FADD34400D85AA5 /* Dwifft.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 383AE65E1FADD1AA002B6725 /* Dwifft.framework */; };
 		3869E24C1FADD34400D85AA5 /* Dwifft.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 383AE65E1FADD1AA002B6725 /* Dwifft.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+		3869E2561FADD5E000D85AA5 /* Stuff.swift in Sources */ = {isa = PBXBuildFile; fileRef = 046EA8A21E8CBB9400FBD07D /* Stuff.swift */; };
+		388BB5361FAE0C2200F2952D /* StuffCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388BB5351FAE0C2200F2952D /* StuffCollectionViewController.swift */; };
+		38B70D321FAF328C0049AEA1 /* StuffCollectionViewItem.xib in Resources */ = {isa = PBXBuildFile; fileRef = 38B70D311FAF328C0049AEA1 /* StuffCollectionViewItem.xib */; };
+		38B70D371FAF32DB0049AEA1 /* StuffCollectionHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 38B70D361FAF32DB0049AEA1 /* StuffCollectionHeaderView.xib */; };
+		38B70D391FAF33E70049AEA1 /* StuffCollectionViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38B70D381FAF33E70049AEA1 /* StuffCollectionViewItem.swift */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -123,11 +128,15 @@
 		046EA8A21E8CBB9400FBD07D /* Stuff.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stuff.swift; sourceTree = "<group>"; };
 		383AE64A1FADD1AA002B6725 /* DwifftExample-macOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DwifftExample-macOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
 		383AE64C1FADD1AA002B6725 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
-		383AE64E1FADD1AA002B6725 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
+		383AE64E1FADD1AA002B6725 /* StuffTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StuffTableViewController.swift; sourceTree = "<group>"; };
 		383AE6501FADD1AA002B6725 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
 		383AE6531FADD1AA002B6725 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
 		383AE6551FADD1AA002B6725 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		383AE6561FADD1AA002B6725 /* DwifftExamples_macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DwifftExamples_macOS.entitlements; sourceTree = "<group>"; };
+		388BB5351FAE0C2200F2952D /* StuffCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StuffCollectionViewController.swift; sourceTree = "<group>"; };
+		38B70D311FAF328C0049AEA1 /* StuffCollectionViewItem.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = StuffCollectionViewItem.xib; sourceTree = "<group>"; };
+		38B70D361FAF32DB0049AEA1 /* StuffCollectionHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = StuffCollectionHeaderView.xib; sourceTree = "<group>"; };
+		38B70D381FAF33E70049AEA1 /* StuffCollectionViewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StuffCollectionViewItem.swift; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -154,6 +163,7 @@
 			isa = PBXGroup;
 			children = (
 				045502181B898C5400F5614D /* Dwifft.xcodeproj */,
+				3869E2551FADD5B600D85AA5 /* Shared */,
 				045501F51B898C3200F5614D /* DwifftExample-iOS */,
 				383AE64B1FADD1AA002B6725 /* DwifftExamples-macOS */,
 				045501F41B898C3200F5614D /* Products */,
@@ -176,7 +186,6 @@
 				0455022A1B898CF000F5614D /* StuffTableViewController.swift */,
 				046EA89F1E8CB1AA00FBD07D /* StuffCollectionViewController.swift */,
 				0401A2081EA08151003C82E5 /* EventsTableViewController.swift */,
-				046EA8A21E8CBB9400FBD07D /* Stuff.swift */,
 				045501FC1B898C3200F5614D /* Main.storyboard */,
 				045502011B898C3200F5614D /* LaunchScreen.xib */,
 				045501F61B898C3200F5614D /* Supporting Files */,
@@ -210,8 +219,12 @@
 			children = (
 				383AE6621FADD237002B6725 /* Supporting Files */,
 				383AE64C1FADD1AA002B6725 /* AppDelegate.swift */,
-				383AE64E1FADD1AA002B6725 /* ViewController.swift */,
+				383AE64E1FADD1AA002B6725 /* StuffTableViewController.swift */,
+				388BB5351FAE0C2200F2952D /* StuffCollectionViewController.swift */,
 				383AE6521FADD1AA002B6725 /* Main.storyboard */,
+				38B70D311FAF328C0049AEA1 /* StuffCollectionViewItem.xib */,
+				38B70D361FAF32DB0049AEA1 /* StuffCollectionHeaderView.xib */,
+				38B70D381FAF33E70049AEA1 /* StuffCollectionViewItem.swift */,
 			);
 			path = "DwifftExamples-macOS";
 			sourceTree = "<group>";
@@ -226,6 +239,14 @@
 			path = "Supporting Files";
 			sourceTree = "<group>";
 		};
+		3869E2551FADD5B600D85AA5 /* Shared */ = {
+			isa = PBXGroup;
+			children = (
+				046EA8A21E8CBB9400FBD07D /* Stuff.swift */,
+			);
+			path = Shared;
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
@@ -368,6 +389,8 @@
 			files = (
 				383AE6511FADD1AA002B6725 /* Assets.xcassets in Resources */,
 				383AE6541FADD1AA002B6725 /* Main.storyboard in Resources */,
+				38B70D321FAF328C0049AEA1 /* StuffCollectionViewItem.xib in Resources */,
+				38B70D371FAF32DB0049AEA1 /* StuffCollectionHeaderView.xib in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -390,8 +413,11 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				383AE64F1FADD1AA002B6725 /* ViewController.swift in Sources */,
+				383AE64F1FADD1AA002B6725 /* StuffTableViewController.swift in Sources */,
+				38B70D391FAF33E70049AEA1 /* StuffCollectionViewItem.swift in Sources */,
+				388BB5361FAE0C2200F2952D /* StuffCollectionViewController.swift in Sources */,
 				383AE64D1FADD1AA002B6725 /* AppDelegate.swift in Sources */,
+				3869E2561FADD5E000D85AA5 /* Stuff.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/DwifftExample/DwifftExamples-macOS/Base.lproj/Main.storyboard b/DwifftExample/DwifftExamples-macOS/Base.lproj/Main.storyboard
index 786bd90..e5f6978 100644
--- a/DwifftExample/DwifftExamples-macOS/Base.lproj/Main.storyboard
+++ b/DwifftExample/DwifftExamples-macOS/Base.lproj/Main.storyboard
@@ -1,7 +1,9 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="13529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
     <dependencies>
-        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11134"/>
+        <deployment identifier="macosx"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13529"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
     <scenes>
         <!--Application-->
@@ -673,7 +675,7 @@
                         <outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
                     </connections>
                 </application>
-                <customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModuleProvider="target"/>
+                <customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="DwifftExample_macOS" customModuleProvider="target"/>
                 <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
                 <customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
             </objects>
@@ -683,35 +685,208 @@
         <scene sceneID="R2V-B0-nI4">
             <objects>
                 <windowController id="B8D-0N-5wS" sceneMemberID="viewController">
-                    <window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="IQv-IB-iLA">
+                    <window key="window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" titlebarAppearsTransparent="YES" titleVisibility="hidden" id="IQv-IB-iLA">
                         <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
-                        <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
                         <rect key="contentRect" x="196" y="240" width="480" height="270"/>
                         <rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
-			<connections>
+                        <connections>
                             <outlet property="delegate" destination="B8D-0N-5wS" id="98r-iN-zZc"/>
                         </connections>
                     </window>
                     <connections>
-                        <segue destination="XfG-lQ-9wD" kind="relationship" relationship="window.shadowedContentViewController" id="cq2-FE-JQM"/>
+                        <segue destination="1vE-e5-vPF" kind="relationship" relationship="window.shadowedContentViewController" id="QpG-rW-KO9"/>
                     </connections>
                 </windowController>
                 <customObject id="Oky-zY-oP4" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
             </objects>
             <point key="canvasLocation" x="75" y="250"/>
         </scene>
-        <!--View Controller-->
-        <scene sceneID="hIz-AP-VOD">
+        <!--Tab View Controller-->
+        <scene sceneID="C9Y-hy-t4v">
             <objects>
-                <viewController id="XfG-lQ-9wD" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
-                    <view key="view" wantsLayer="YES" id="m2S-Jp-Qdl">
-                        <rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
+                <tabViewController selectedTabViewItemIndex="1" id="1vE-e5-vPF" sceneMemberID="viewController">
+                    <tabViewItems>
+                        <tabViewItem label="Table" identifier="" id="BEc-Vo-WXE"/>
+                        <tabViewItem label="Collection" identifier="" id="mpK-w8-Zbh"/>
+                    </tabViewItems>
+                    <tabView key="tabView" type="noTabsNoBorder" id="uPM-IG-FJw">
+                        <rect key="frame" x="1" y="0.0" width="479" height="300"/>
                         <autoresizingMask key="autoresizingMask"/>
+                        <font key="font" metaFont="message"/>
+                        <tabViewItems/>
+                        <connections>
+                            <outlet property="delegate" destination="1vE-e5-vPF" id="WyC-Z8-NqD"/>
+                        </connections>
+                    </tabView>
+                    <connections>
+                        <outlet property="tabView" destination="uPM-IG-FJw" id="Y4H-FZ-7d9"/>
+                        <segue destination="NO0-Lb-Myr" kind="relationship" relationship="tabItems" id="9V5-Z9-Fcl"/>
+                        <segue destination="D4b-NX-Tgs" kind="relationship" relationship="tabItems" id="k2n-Po-Mig"/>
+                    </connections>
+                </tabViewController>
+                <customObject id="FK4-eL-cge" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="76" y="731"/>
+        </scene>
+        <!--Stuff Table View Controller-->
+        <scene sceneID="Zcc-mP-2lQ">
+            <objects>
+                <viewController id="NO0-Lb-Myr" customClass="StuffTableViewController" customModule="DwifftExample_macOS" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" id="4GF-HL-GiL">
+                        <rect key="frame" x="0.0" y="0.0" width="452" height="300"/>
+                        <autoresizingMask key="autoresizingMask"/>
+                        <subviews>
+                            <scrollView autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="148-Wq-1BV">
+                                <rect key="frame" x="20" y="61" width="412" height="219"/>
+                                <clipView key="contentView" id="laV-7c-1ce">
+                                    <rect key="frame" x="1" y="1" width="410" height="217"/>
+                                    <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                                    <subviews>
+                                        <tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" viewBased="YES" id="GQD-JO-4SI">
+                                            <rect key="frame" x="0.0" y="0.0" width="410" height="217"/>
+                                            <autoresizingMask key="autoresizingMask"/>
+                                            <size key="intercellSpacing" width="3" height="2"/>
+                                            <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+                                            <color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
+                                            <tableColumns>
+                                                <tableColumn identifier="" width="407" minWidth="40" maxWidth="1000" id="cqv-5U-hYF">
+                                                    <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Item">
+                                                        <font key="font" metaFont="smallSystem"/>
+                                                        <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
+                                                        <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
+                                                    </tableHeaderCell>
+                                                    <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="xYB-Ls-7J8">
+                                                        <font key="font" metaFont="system"/>
+                                                        <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+                                                        <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+                                                    </textFieldCell>
+                                                    <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
+                                                    <prototypeCellViews>
+                                                        <tableCellView identifier="itemCell" id="NW6-Oe-6lP">
+                                                            <rect key="frame" x="1" y="1" width="407" height="17"/>
+                                                            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                                                            <subviews>
+                                                                <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="oox-rg-dVZ">
+                                                                    <rect key="frame" x="0.0" y="0.0" width="407" height="17"/>
+                                                                    <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
+                                                                    <textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="WPD-3O-dyV">
+                                                                        <font key="font" metaFont="system"/>
+                                                                        <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+                                                                        <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+                                                                    </textFieldCell>
+                                                                </textField>
+                                                            </subviews>
+                                                            <connections>
+                                                                <outlet property="textField" destination="oox-rg-dVZ" id="Qm4-IV-qeV"/>
+                                                            </connections>
+                                                        </tableCellView>
+                                                    </prototypeCellViews>
+                                                </tableColumn>
+                                            </tableColumns>
+                                        </tableView>
+                                    </subviews>
+                                </clipView>
+                                <scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="NmN-wb-aEl">
+                                    <rect key="frame" x="1" y="202" width="408" height="16"/>
+                                    <autoresizingMask key="autoresizingMask"/>
+                                </scroller>
+                                <scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="efU-OM-CWa">
+                                    <rect key="frame" x="224" y="17" width="15" height="102"/>
+                                    <autoresizingMask key="autoresizingMask"/>
+                                </scroller>
+                            </scrollView>
+                            <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ieA-Rb-fol">
+                                <rect key="frame" x="354" y="13" width="84" height="32"/>
+                                <buttonCell key="cell" type="push" title="Shuffle" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="XFi-bw-0AY">
+                                    <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+                                    <font key="font" metaFont="system"/>
+                                </buttonCell>
+                                <connections>
+                                    <action selector="shuffle:" target="NO0-Lb-Myr" id="Y9Y-84-VLz"/>
+                                </connections>
+                            </button>
+                        </subviews>
+                        <constraints>
+                            <constraint firstAttribute="bottom" secondItem="ieA-Rb-fol" secondAttribute="bottom" constant="20" id="0t3-87-QTG"/>
+                            <constraint firstItem="148-Wq-1BV" firstAttribute="leading" secondItem="4GF-HL-GiL" secondAttribute="leading" constant="20" id="6J6-Lf-WjB"/>
+                            <constraint firstAttribute="bottom" secondItem="148-Wq-1BV" secondAttribute="bottom" constant="61" id="IKB-5D-Ncd"/>
+                            <constraint firstAttribute="trailing" secondItem="ieA-Rb-fol" secondAttribute="trailing" constant="20" id="drS-U1-GQx"/>
+                            <constraint firstItem="ieA-Rb-fol" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="4GF-HL-GiL" secondAttribute="leading" constant="20" symbolic="YES" id="i6t-3p-WrW"/>
+                            <constraint firstItem="148-Wq-1BV" firstAttribute="top" secondItem="4GF-HL-GiL" secondAttribute="top" constant="20" id="jX9-td-fXH"/>
+                            <constraint firstAttribute="trailing" secondItem="148-Wq-1BV" secondAttribute="trailing" constant="20" id="sN1-cA-JdP"/>
+                        </constraints>
                     </view>
+                    <connections>
+                        <outlet property="tableView" destination="GQD-JO-4SI" id="2ee-YP-rdv"/>
+                    </connections>
+                </viewController>
+                <customObject id="eBl-L0-ols" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="730" y="543"/>
+        </scene>
+        <!--Stuff Collection View Controller-->
+        <scene sceneID="Lwg-Tq-ZqY">
+            <objects>
+                <viewController id="D4b-NX-Tgs" customClass="StuffCollectionViewController" customModule="DwifftExample_macOS" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" id="jFT-PR-U1p">
+                        <rect key="frame" x="0.0" y="0.0" width="450" height="300"/>
+                        <autoresizingMask key="autoresizingMask"/>
+                        <subviews>
+                            <scrollView wantsLayer="YES" autohidesScrollers="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Vap-vo-g3h">
+                                <rect key="frame" x="20" y="61" width="410" height="219"/>
+                                <clipView key="contentView" drawsBackground="NO" id="egF-wW-jbh">
+                                    <rect key="frame" x="1" y="1" width="408" height="217"/>
+                                    <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                                    <subviews>
+                                        <collectionView id="zFa-bq-fRd">
+                                            <rect key="frame" x="0.0" y="0.0" width="408" height="158"/>
+                                            <autoresizingMask key="autoresizingMask" widthSizable="YES"/>
+                                            <collectionViewFlowLayout key="collectionViewLayout" minimumInteritemSpacing="10" minimumLineSpacing="10" id="INc-yc-TRN">
+                                                <size key="itemSize" width="60" height="60"/>
+                                                <size key="headerReferenceSize" width="50" height="50"/>
+                                            </collectionViewFlowLayout>
+                                            <color key="primaryBackgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+                                        </collectionView>
+                                    </subviews>
+                                    <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+                                </clipView>
+                                <scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="Mki-xa-dNm">
+                                    <rect key="frame" x="1" y="144" width="233" height="15"/>
+                                    <autoresizingMask key="autoresizingMask"/>
+                                </scroller>
+                                <scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="qY5-WD-sTp">
+                                    <rect key="frame" x="234" y="1" width="15" height="143"/>
+                                    <autoresizingMask key="autoresizingMask"/>
+                                </scroller>
+                            </scrollView>
+                            <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="16r-of-nPT">
+                                <rect key="frame" x="352" y="13" width="84" height="32"/>
+                                <buttonCell key="cell" type="push" title="Shuffle" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="WVI-bb-vEo">
+                                    <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+                                    <font key="font" metaFont="system"/>
+                                </buttonCell>
+                                <connections>
+                                    <action selector="shuffle:" target="D4b-NX-Tgs" id="r2H-PH-ItM"/>
+                                </connections>
+                            </button>
+                        </subviews>
+                        <constraints>
+                            <constraint firstAttribute="bottom" secondItem="Vap-vo-g3h" secondAttribute="bottom" constant="61" id="1fE-Ac-wTQ"/>
+                            <constraint firstAttribute="bottom" secondItem="16r-of-nPT" secondAttribute="bottom" constant="20" id="2gu-c0-krn"/>
+                            <constraint firstItem="Vap-vo-g3h" firstAttribute="leading" secondItem="jFT-PR-U1p" secondAttribute="leading" constant="20" id="5l3-GI-vHU"/>
+                            <constraint firstItem="16r-of-nPT" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="jFT-PR-U1p" secondAttribute="leading" constant="20" symbolic="YES" id="aAR-zP-Zrj"/>
+                            <constraint firstAttribute="trailing" secondItem="Vap-vo-g3h" secondAttribute="trailing" constant="20" id="aKp-4z-2mV"/>
+                            <constraint firstAttribute="trailing" secondItem="16r-of-nPT" secondAttribute="trailing" constant="20" id="jna-g3-rQG"/>
+                            <constraint firstItem="Vap-vo-g3h" firstAttribute="top" secondItem="jFT-PR-U1p" secondAttribute="top" constant="20" id="pQ1-PA-KAD"/>
+                        </constraints>
+                    </view>
+                    <connections>
+                        <outlet property="collectionView" destination="zFa-bq-fRd" id="ODf-gD-YEa"/>
+                    </connections>
                 </viewController>
-                <customObject id="rPt-NT-nkU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
+                <customObject id="BTX-cg-Lsx" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
             </objects>
-            <point key="canvasLocation" x="75" y="655"/>
+            <point key="canvasLocation" x="730" y="918"/>
         </scene>
     </scenes>
 </document>
diff --git a/DwifftExample/DwifftExamples-macOS/StuffCollectionHeaderView.xib b/DwifftExample/DwifftExamples-macOS/StuffCollectionHeaderView.xib
new file mode 100644
index 0000000..9c82bdc
--- /dev/null
+++ b/DwifftExample/DwifftExamples-macOS/StuffCollectionHeaderView.xib
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+    <dependencies>
+        <deployment identifier="macosx"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13529"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+        <capability name="system font weights other than Regular or Bold" minToolsVersion="7.0"/>
+    </dependencies>
+    <objects>
+        <customObject id="-2" userLabel="File's Owner"/>
+        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+        <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+        <customView id="c22-O7-iKe" customClass="StuffCollectionHeaderView" customModule="DwifftExample_macOS" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="480" height="34"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+            <subviews>
+                <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ehT-LL-UG8">
+                    <rect key="frame" x="220" y="8" width="40" height="18"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="18" id="viC-5X-w9G"/>
+                    </constraints>
+                    <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Label" id="bBz-Tm-jfv">
+                        <font key="font" metaFont="systemMedium" size="14"/>
+                        <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                        <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+                    </textFieldCell>
+                </textField>
+            </subviews>
+            <constraints>
+                <constraint firstItem="ehT-LL-UG8" firstAttribute="centerY" secondItem="c22-O7-iKe" secondAttribute="centerY" id="IPY-GS-NE6"/>
+                <constraint firstItem="ehT-LL-UG8" firstAttribute="centerX" secondItem="c22-O7-iKe" secondAttribute="centerX" id="SKR-Qj-9U9"/>
+            </constraints>
+            <connections>
+                <outlet property="title" destination="ehT-LL-UG8" id="fTw-ZX-hqG"/>
+            </connections>
+            <point key="canvasLocation" x="139" y="211"/>
+        </customView>
+    </objects>
+</document>
diff --git a/DwifftExample/DwifftExamples-macOS/StuffCollectionViewController.swift b/DwifftExample/DwifftExamples-macOS/StuffCollectionViewController.swift
new file mode 100644
index 0000000..49ab94d
--- /dev/null
+++ b/DwifftExample/DwifftExamples-macOS/StuffCollectionViewController.swift
@@ -0,0 +1,69 @@
+import Cocoa
+import Dwifft
+
+final class StuffCollectionViewController: NSViewController {
+
+  @IBOutlet weak var collectionView: NSCollectionView!
+
+  let itemIdentifier: NSUserInterfaceItemIdentifier = NSUserInterfaceItemIdentifier("emojiItem")
+  let headerIdentifier: NSUserInterfaceItemIdentifier = NSUserInterfaceItemIdentifier("sectionHeader")
+  var diffCalculator: CollectionViewDiffCalculator<String, String>?
+  var stuff: SectionedValues<String, String> = Stuff.emojiStuff() {
+    // So, whenever your datasource's array of things changes, just let the diffCalculator know and it'll do the rest.
+    didSet {
+      self.diffCalculator?.sectionedValues = stuff
+    }
+  }
+
+  override func viewDidLoad() {
+    super.viewDidLoad()
+    collectionView.dataSource = self
+    diffCalculator = CollectionViewDiffCalculator(collectionView: collectionView, initialSectionedValues: stuff)
+
+    let itemNib = NSNib(nibNamed: NSNib.Name(rawValue: "StuffCollectionViewItem"), bundle: nil)
+    collectionView.register(itemNib, forItemWithIdentifier: itemIdentifier)
+
+    let headerNib = NSNib(nibNamed: NSNib.Name(rawValue: "StuffCollectionHeaderView"), bundle: nil)
+    collectionView.register(headerNib, forSupplementaryViewOfKind: .sectionHeader, withIdentifier: headerIdentifier)
+  }
+
+  @IBAction func shuffle(_ sender: NSButton) {
+    self.stuff = Stuff.emojiStuff()
+  }
+}
+
+extension StuffCollectionViewController: NSCollectionViewDataSource {
+  func numberOfSections(in collectionView: NSCollectionView) -> Int {
+    return diffCalculator?.numberOfSections() ?? 0
+  }
+
+  func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
+    return diffCalculator?.numberOfObjects(inSection: section) ?? 0
+  }
+
+  func collectionView(_ collectionView: NSCollectionView,
+                      viewForSupplementaryElementOfKind kind: NSCollectionView.SupplementaryElementKind,
+                      at indexPath: IndexPath) -> NSView {
+    let header = collectionView.makeSupplementaryView(ofKind: .sectionHeader, withIdentifier: headerIdentifier, for: indexPath)
+
+    if let dc = diffCalculator, let stuffHeader = header as? StuffCollectionHeaderView {
+      stuffHeader.title.stringValue = dc.value(forSection: indexPath.section)
+    }
+
+    return header
+  }
+
+  func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
+    let item = collectionView.makeItem(withIdentifier: itemIdentifier, for: indexPath)
+
+    if let dc = diffCalculator, let emojiItem = item as? StuffCollectionViewItem {
+      emojiItem.textField?.stringValue = dc.value(atIndexPath: indexPath)
+    }
+
+    return item
+  }
+}
+
+final class StuffCollectionHeaderView: NSView {
+  @IBOutlet weak var title: NSTextField!
+}
diff --git a/DwifftExample/DwifftExamples-macOS/StuffCollectionViewItem.swift b/DwifftExample/DwifftExamples-macOS/StuffCollectionViewItem.swift
new file mode 100644
index 0000000..c1e9998
--- /dev/null
+++ b/DwifftExample/DwifftExamples-macOS/StuffCollectionViewItem.swift
@@ -0,0 +1,3 @@
+import Cocoa
+
+final class StuffCollectionViewItem: NSCollectionViewItem { }
diff --git a/DwifftExample/DwifftExamples-macOS/StuffCollectionViewItem.xib b/DwifftExample/DwifftExamples-macOS/StuffCollectionViewItem.xib
new file mode 100644
index 0000000..6b8aa93
--- /dev/null
+++ b/DwifftExample/DwifftExamples-macOS/StuffCollectionViewItem.xib
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+    <dependencies>
+        <deployment identifier="macosx"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13529"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <customObject id="-2" userLabel="File's Owner"/>
+        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+        <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+        <customView id="c22-O7-iKe">
+            <rect key="frame" x="0.0" y="0.0" width="480" height="61"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+            <subviews>
+                <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="aMz-N2-Abt">
+                    <rect key="frame" x="3" y="5" width="474" height="51"/>
+                    <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Label" id="xaJ-Dv-YOV">
+                        <font key="font" metaFont="system" size="42"/>
+                        <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                        <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+                    </textFieldCell>
+                </textField>
+            </subviews>
+            <constraints>
+                <constraint firstAttribute="bottom" secondItem="aMz-N2-Abt" secondAttribute="bottom" constant="5" id="31x-s3-cuB"/>
+                <constraint firstAttribute="trailing" secondItem="aMz-N2-Abt" secondAttribute="trailing" constant="5" id="Dqo-8T-Le8"/>
+                <constraint firstItem="aMz-N2-Abt" firstAttribute="leading" secondItem="c22-O7-iKe" secondAttribute="leading" constant="5" id="k8O-oW-Bfb"/>
+                <constraint firstItem="aMz-N2-Abt" firstAttribute="top" secondItem="c22-O7-iKe" secondAttribute="top" constant="5" id="qip-ih-g1a"/>
+            </constraints>
+        </customView>
+        <collectionViewItem id="U4V-H6-ESQ" customClass="StuffCollectionViewItem" customModule="DwifftExample_macOS" customModuleProvider="target">
+            <connections>
+                <outlet property="textField" destination="aMz-N2-Abt" id="83J-A6-7Bp"/>
+                <outlet property="view" destination="c22-O7-iKe" id="tA2-J5-gac"/>
+            </connections>
+        </collectionViewItem>
+    </objects>
+</document>
diff --git a/DwifftExample/DwifftExamples-macOS/StuffTableViewController.swift b/DwifftExample/DwifftExamples-macOS/StuffTableViewController.swift
new file mode 100644
index 0000000..ef60278
--- /dev/null
+++ b/DwifftExample/DwifftExamples-macOS/StuffTableViewController.swift
@@ -0,0 +1,46 @@
+import Cocoa
+import Dwifft
+
+final class StuffTableViewController: NSViewController {
+
+  @IBOutlet weak var tableView: NSTableView!
+
+  let itemIdentifier: NSUserInterfaceItemIdentifier = NSUserInterfaceItemIdentifier("itemCell")
+  var diffCalculator: TableViewDiffCalculator<String>?
+  var stuff: [String] = Stuff.onlyWordItems() {
+    // So, whenever your datasource's array of things changes, just let the diffCalculator know and it'll do the rest.
+    didSet {
+      self.diffCalculator?.rows = stuff
+    }
+  }
+
+  @IBAction func shuffle(_ sender: NSButton) {
+    self.stuff = Stuff.onlyWordItems()
+  }
+
+  override func viewDidLoad() {
+    super.viewDidLoad()
+    diffCalculator = TableViewDiffCalculator(tableView: tableView, initialRows: stuff, sectionIndex: 1)
+    tableView.delegate = self
+    tableView.dataSource = self
+  }
+}
+
+extension StuffTableViewController: NSTableViewDataSource {
+  func numberOfRows(in tableView: NSTableView) -> Int {
+    return diffCalculator?.rows.count ?? 0
+  }
+}
+
+extension StuffTableViewController: NSTableViewDelegate {
+  func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
+    guard
+      let item = tableView.makeView(withIdentifier: itemIdentifier, owner: nil) as? NSTableCellView,
+      let word = diffCalculator?.rows[row]
+    else {
+      return nil
+    }
+    item.textField?.stringValue = word
+    return item
+  }
+}
diff --git a/DwifftExample/DwifftExamples-macOS/ViewController.swift b/DwifftExample/DwifftExamples-macOS/ViewController.swift
deleted file mode 100644
index b99a9d0..0000000
--- a/DwifftExample/DwifftExamples-macOS/ViewController.swift
+++ /dev/null
@@ -1,21 +0,0 @@
-//
-
-import Cocoa
-
-class ViewController: NSViewController {
-
-  override func viewDidLoad() {
-    super.viewDidLoad()
-
-    // Do any additional setup after loading the view.
-  }
-
-  override var representedObject: Any? {
-    didSet {
-    // Update the view, if already loaded.
-    }
-  }
-
-
-}
-
diff --git a/DwifftExample/DwifftExample-iOS/Stuff.swift b/DwifftExample/Shared/Stuff.swift
similarity index 93%
rename from DwifftExample/DwifftExample-iOS/Stuff.swift
rename to DwifftExample/Shared/Stuff.swift
index 4b106b3..78646b3 100644
--- a/DwifftExample/DwifftExample-iOS/Stuff.swift
+++ b/DwifftExample/Shared/Stuff.swift
@@ -6,6 +6,7 @@
 //  Copyright © 2017 jflinter. All rights reserved.
 //
 
+import Foundation
 import Dwifft
 
 struct Stuff {
@@ -40,6 +41,10 @@ struct Stuff {
         return SectionedValues(mutable)
     }
 
+    static func onlyWordItems() -> [String] {
+        return Stuff.wordStuff().sectionsAndValues.flatMap() { $0.1 }
+    }
+
     static func emojiStuff() -> SectionedValues<String, String> {
         let possibleStuff = [
             ("foods", [
@@ -69,4 +74,5 @@ struct Stuff {
         }
         return SectionedValues(mutable)
     }
+
 }

From 34c232ab7e609512f8967b2f1506888a464546a1 Mon Sep 17 00:00:00 2001
From: Rik Chilvers <rikchilvers@fastmail.com>
Date: Sun, 5 Nov 2017 13:55:28 +0000
Subject: [PATCH 31/48] reinstate test dependencies

---
 Cartfile.resolved                |  3 ++-
 Dwifft.xcodeproj/project.pbxproj | 33 ++++++++++++++++++++++++--------
 2 files changed, 27 insertions(+), 9 deletions(-)

diff --git a/Cartfile.resolved b/Cartfile.resolved
index 97fb7bd..6b0a63e 100644
--- a/Cartfile.resolved
+++ b/Cartfile.resolved
@@ -1 +1,2 @@
-github "typelift/SwiftCheck" "0.8.1"
+github "trill-lang/FileCheck" "0.0.4"
+github "typelift/SwiftCheck" "0.9.1"
diff --git a/Dwifft.xcodeproj/project.pbxproj b/Dwifft.xcodeproj/project.pbxproj
index 3fb299a..be2fcf6 100644
--- a/Dwifft.xcodeproj/project.pbxproj
+++ b/Dwifft.xcodeproj/project.pbxproj
@@ -22,6 +22,8 @@
 		382DEF4B1F487519007A8FD2 /* Dwifft.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041EBBAE1B8967C300E113B3 /* Dwifft.swift */; };
 		382DEF4C1F48751B007A8FD2 /* SectionedValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0486A2641EA0A5B600D8093E /* SectionedValues.swift */; };
 		382DEF4D1F48751C007A8FD2 /* Dwifft+AppKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 382DEF4A1F4873F9007A8FD2 /* Dwifft+AppKit.swift */; };
+		38B70D3E1FAF4F800049AEA1 /* SwiftCheck.framework in Resources */ = {isa = PBXBuildFile; fileRef = 38D317731F7BF1E400A15CD7 /* SwiftCheck.framework */; };
+		38B70D3F1FAF4F930049AEA1 /* SwiftCheck.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 38D317731F7BF1E400A15CD7 /* SwiftCheck.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		38E5B5D61F7BDB5400F33285 /* SectionedValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0486A2641EA0A5B600D8093E /* SectionedValues.swift */; };
 		38E5B5D71F7BDB5400F33285 /* Dwifft.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041EBBAE1B8967C300E113B3 /* Dwifft.swift */; };
 		38E5B5E91F7BDBA500F33285 /* Dwifft.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 382DEF421F487395007A8FD2 /* Dwifft.framework */; };
@@ -50,6 +52,19 @@
 		};
 /* End PBXContainerItemProxy section */
 
+/* Begin PBXCopyFilesBuildPhase section */
+		38B70D3D1FAF4F790049AEA1 /* CopyFiles */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 10;
+			files = (
+				38B70D3F1FAF4F930049AEA1 /* SwiftCheck.framework in CopyFiles */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
 /* Begin PBXFileReference section */
 		041EBB921B89679200E113B3 /* Dwifft.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Dwifft.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		041EBB961B89679200E113B3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -91,13 +106,6 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
-		382DEF3E1F487395007A8FD2 /* Frameworks */ = {
-			isa = PBXFrameworksBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
 		38E5B5DA1F7BDB5400F33285 /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
@@ -288,7 +296,6 @@
 			buildConfigurationList = 382DEF491F487396007A8FD2 /* Build configuration list for PBXNativeTarget "Dwifft-macOS" */;
 			buildPhases = (
 				382DEF3D1F487395007A8FD2 /* Sources */,
-				382DEF3E1F487395007A8FD2 /* Frameworks */,
 				382DEF3F1F487395007A8FD2 /* Headers */,
 			);
 			buildRules = (
@@ -307,6 +314,7 @@
 				38E5B5D51F7BDB5400F33285 /* Sources */,
 				38E5B5DA1F7BDB5400F33285 /* Frameworks */,
 				38E5B5DD1F7BDB5400F33285 /* Resources */,
+				38B70D3D1FAF4F790049AEA1 /* CopyFiles */,
 			);
 			buildRules = (
 			);
@@ -405,6 +413,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				38B70D3E1FAF4F800049AEA1 /* SwiftCheck.framework in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -703,6 +712,10 @@
 				DYLIB_COMPATIBILITY_VERSION = 1;
 				DYLIB_CURRENT_VERSION = 1;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Carthage/Build/Mac",
+				);
 				FRAMEWORK_VERSION = A;
 				GCC_C_LANGUAGE_STANDARD = gnu11;
 				INFOPLIST_FILE = "Dwifft-macOS/Info.plist";
@@ -734,6 +747,10 @@
 				DYLIB_COMPATIBILITY_VERSION = 1;
 				DYLIB_CURRENT_VERSION = 1;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Carthage/Build/Mac",
+				);
 				FRAMEWORK_VERSION = A;
 				GCC_C_LANGUAGE_STANDARD = gnu11;
 				INFOPLIST_FILE = "Dwifft-macOS/Info.plist";

From b7a804b113a8c77cd3233f51f400f85a2e5a1c91 Mon Sep 17 00:00:00 2001
From: Jack Flintermann <jack@stripe.com>
Date: Mon, 11 Dec 2017 17:55:11 -0800
Subject: [PATCH 32/48] tweaks

---
 Dwifft.podspec                   | 2 --
 Dwifft.xcodeproj/project.pbxproj | 4 ----
 Dwifft/Dwifft.swift              | 5 +++--
 3 files changed, 3 insertions(+), 8 deletions(-)

diff --git a/Dwifft.podspec b/Dwifft.podspec
index dedf069..eb007a2 100644
--- a/Dwifft.podspec
+++ b/Dwifft.podspec
@@ -14,6 +14,4 @@ Pod::Spec.new do |s|
   s.source_files = 'Dwifft/*.swift'
 
   s.requires_arc = true
-
-  s.pod_target_xcconfig = { 'SWIFT_VERSION' => '3.0.1' }
 end
diff --git a/Dwifft.xcodeproj/project.pbxproj b/Dwifft.xcodeproj/project.pbxproj
index 92c8733..8389f28 100644
--- a/Dwifft.xcodeproj/project.pbxproj
+++ b/Dwifft.xcodeproj/project.pbxproj
@@ -469,7 +469,6 @@
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
-				SWIFT_SWIFT3_OBJC_INFERENCE = On;
 				SWIFT_VERSION = 4.0;
 			};
 			name = Debug;
@@ -489,7 +488,6 @@
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
-				SWIFT_SWIFT3_OBJC_INFERENCE = On;
 				SWIFT_VERSION = 4.0;
 			};
 			name = Release;
@@ -509,7 +507,6 @@
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SWIFT_SWIFT3_OBJC_INFERENCE = On;
 				SWIFT_VERSION = 4.0;
 			};
 			name = Debug;
@@ -526,7 +523,6 @@
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
-				SWIFT_SWIFT3_OBJC_INFERENCE = On;
 				SWIFT_VERSION = 4.0;
 			};
 			name = Release;
diff --git a/Dwifft/Dwifft.swift b/Dwifft/Dwifft.swift
index 4ed7a4a..188d037 100644
--- a/Dwifft/Dwifft.swift
+++ b/Dwifft/Dwifft.swift
@@ -91,7 +91,7 @@ public enum Dwifft {
             return lhs.enumerated().map(DiffStep.delete).reversed()
         }
 
-        let table = MemoizedSequenceComparison.buildTable(lhs, rhs, lhs.count, rhs.count)
+        let table = MemoizedSequenceComparison.buildTable(lhs, rhs)
         var result = diffInternal(table, lhs, rhs, lhs.count, rhs.count, ([], []))
         while case let .call(f) = result {
             result = f()
@@ -279,7 +279,8 @@ fileprivate enum Result<T>{
 }
 
 fileprivate struct MemoizedSequenceComparison<T: Equatable> {
-    static func buildTable(_ x: [T], _ y: [T], _ n: Int, _ m: Int) -> [[Int]] {
+    static func buildTable(_ x: [T], _ y: [T]) -> [[Int]] {
+        let n = x.count, m = y.count
         var table = Array(repeating: Array(repeating: 0, count: m + 1), count: n + 1)
         // using unsafe pointers lets us avoid swift array bounds-checking, which results in a considerable speed boost.
         table.withUnsafeMutableBufferPointer { unsafeTable in

From 6e16ae3b85504a4fa4f0da39f3a7275377af8fd2 Mon Sep 17 00:00:00 2001
From: Jack Flintermann <jack@stripe.com>
Date: Mon, 11 Dec 2017 19:21:12 -0800
Subject: [PATCH 33/48] cleanup & test fixes

---
 .travis.yml                         |   2 +
 Dwifft-macOS/Dwifft_macOS.h         |   4 -
 Dwifft.xcodeproj/project.pbxproj    |  12 ++
 Dwifft/AbstractDiffCalculator.swift |  93 ++++++++++
 Dwifft/Dwifft+AppKit.swift          | 256 ++++++++--------------------
 Dwifft/Dwifft+UIKit.swift           |  96 +----------
 DwifftTests/DwifftTests-macOS.swift |   8 +-
 7 files changed, 196 insertions(+), 275 deletions(-)
 create mode 100644 Dwifft/AbstractDiffCalculator.swift

diff --git a/.travis.yml b/.travis.yml
index 79609b4..e5098ab 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,6 +13,8 @@ matrix:
         - set -o pipefail;
         - open -a "simulator" --args -CurrentDeviceUDID "$UUID"
         - xcodebuild -scheme Dwifft -destination "id=$UUID" test | xcpretty
+        - xcodebuild -scheme Dwifft-macOS test | xcpretty
+
 env:
   global:
     secure: pUV8Ccwq1FWO8PxTHPQ3qZDUDhjNOisNVRyrziYR8x0ZQYjmVmnaLKaOHFP8co6rhBlFqReKUjoFYgRqP5QK1EkD6lqSZfnkrWqnZuLDNy0sn3/+4sLtvqAhBroAfjmmvb5GpY+Kkqfz8Lhdu6+1Z/a5xPhqNeDO3aDpa2vdBztb2qLIZnf2g8NFLOoNuR+ula868nIm858LCN1sqLp9PhV4CGaMsFx7ZFaX1yEQbwNb8+85+U2HIHqjZ62u9KZ4lA1d58VVDGj2f7ObKkC+pjiuknljy5bvRVvg5pttXggJracFgMqouwwQFFCV3nFYSDS6h7ZIjUvbyMrqBN+u32RmtqVLp1by1JvRXSeemJ3HxtZGLbq7rff4zWXyYbelT6sR6J1tWIubRo5v3sXc8E1kurqKkcPqJdG4jqiUXOau2oSHhf7WCRwa0KNfvaQuMhm0Onnsi9tW2MzGieumscfuFIJsXJdXnac7jQUVb571GfxMrDeJ9v2GOPcbnlM8cttBFAw4IoINV6teKITUW6T8RpeDXzfQyxDDQaV0M1ZTab1Tj/f4EYDAXKfZ+QquWK3bgXJhxKghXIskZLdDhYMyP55T6QxLZY9wD2CxLrEbEhDQCEb+7R2LmIC95Rlku+W92b8eWAqNxf+vV7QRqBb/sV9w2mlxmmezYTP5VAs=
diff --git a/Dwifft-macOS/Dwifft_macOS.h b/Dwifft-macOS/Dwifft_macOS.h
index 3bf8a9c..e4ddce0 100644
--- a/Dwifft-macOS/Dwifft_macOS.h
+++ b/Dwifft-macOS/Dwifft_macOS.h
@@ -12,7 +12,3 @@ FOUNDATION_EXPORT double Dwifft_macOSVersionNumber;
 
 //! Project version string for Dwifft_macOS.
 FOUNDATION_EXPORT const unsigned char Dwifft_macOSVersionString[];
-
-// In this header, you should import all the public headers of your framework using statements like #import <Dwifft_macOS/PublicHeader.h>
-
-
diff --git a/Dwifft.xcodeproj/project.pbxproj b/Dwifft.xcodeproj/project.pbxproj
index e4d0222..6542a34 100644
--- a/Dwifft.xcodeproj/project.pbxproj
+++ b/Dwifft.xcodeproj/project.pbxproj
@@ -14,9 +14,14 @@
 		041EBBB11B8967C300E113B3 /* Dwifft.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041EBBAE1B8967C300E113B3 /* Dwifft.swift */; };
 		041EBBB21B8967C300E113B3 /* Dwifft+UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041EBBAF1B8967C300E113B3 /* Dwifft+UIKit.swift */; };
 		041EBBB31B8967C300E113B3 /* Dwifft+UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041EBBAF1B8967C300E113B3 /* Dwifft+UIKit.swift */; };
+		044079181FDF75FF00DDB952 /* AbstractDiffCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 044079171FDF75FF00DDB952 /* AbstractDiffCalculator.swift */; };
+		044079191FDF75FF00DDB952 /* AbstractDiffCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 044079171FDF75FF00DDB952 /* AbstractDiffCalculator.swift */; };
+		0440791A1FDF75FF00DDB952 /* AbstractDiffCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 044079171FDF75FF00DDB952 /* AbstractDiffCalculator.swift */; };
 		0486A2651EA0A5B600D8093E /* SectionedValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0486A2641EA0A5B600D8093E /* SectionedValues.swift */; };
 		0486A2661EA0A64900D8093E /* SectionedValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0486A2641EA0A5B600D8093E /* SectionedValues.swift */; };
 		04AC329F1E88AEB000EF63DD /* SwiftCheck.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04AC329E1E88AEB000EF63DD /* SwiftCheck.framework */; };
+		04CEEB8F1FDF787B00C17562 /* AbstractDiffCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 044079171FDF75FF00DDB952 /* AbstractDiffCalculator.swift */; };
+		04CEEB901FDF787D00C17562 /* AbstractDiffCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 044079171FDF75FF00DDB952 /* AbstractDiffCalculator.swift */; };
 		380FFA411F7BF2E000AEF983 /* SwiftCheck.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 38D317731F7BF1E400A15CD7 /* SwiftCheck.framework */; };
 		382DEF461F487396007A8FD2 /* Dwifft_macOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 382DEF441F487395007A8FD2 /* Dwifft_macOS.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		382DEF4B1F487519007A8FD2 /* Dwifft.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041EBBAE1B8967C300E113B3 /* Dwifft.swift */; };
@@ -74,6 +79,7 @@
 		041EBBA41B89679200E113B3 /* DwifftTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DwifftTests.swift; sourceTree = "<group>"; };
 		041EBBAE1B8967C300E113B3 /* Dwifft.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Dwifft.swift; sourceTree = "<group>"; };
 		041EBBAF1B8967C300E113B3 /* Dwifft+UIKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Dwifft+UIKit.swift"; sourceTree = "<group>"; };
+		044079171FDF75FF00DDB952 /* AbstractDiffCalculator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AbstractDiffCalculator.swift; sourceTree = "<group>"; };
 		0486A2641EA0A5B600D8093E /* SectionedValues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionedValues.swift; sourceTree = "<group>"; };
 		04AC329E1E88AEB000EF63DD /* SwiftCheck.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftCheck.framework; path = Carthage/Build/iOS/SwiftCheck.framework; sourceTree = SOURCE_ROOT; };
 		380FFA421F7C072D00AEF983 /* macOS Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "macOS Info.plist"; sourceTree = "<group>"; };
@@ -155,6 +161,7 @@
 				041EBB971B89679200E113B3 /* Dwifft.h */,
 				041EBBAE1B8967C300E113B3 /* Dwifft.swift */,
 				0486A2641EA0A5B600D8093E /* SectionedValues.swift */,
+				044079171FDF75FF00DDB952 /* AbstractDiffCalculator.swift */,
 				041EBBAF1B8967C300E113B3 /* Dwifft+UIKit.swift */,
 				382DEF4A1F4873F9007A8FD2 /* Dwifft+AppKit.swift */,
 				041EBB951B89679200E113B3 /* Supporting Files */,
@@ -451,6 +458,7 @@
 				041EBBB01B8967C300E113B3 /* Dwifft.swift in Sources */,
 				0486A2651EA0A5B600D8093E /* SectionedValues.swift in Sources */,
 				041EBBB21B8967C300E113B3 /* Dwifft+UIKit.swift in Sources */,
+				044079181FDF75FF00DDB952 /* AbstractDiffCalculator.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -459,6 +467,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				0486A2661EA0A64900D8093E /* SectionedValues.swift in Sources */,
+				04CEEB8F1FDF787B00C17562 /* AbstractDiffCalculator.swift in Sources */,
 				041EBBB11B8967C300E113B3 /* Dwifft.swift in Sources */,
 				041EBBA51B89679200E113B3 /* DwifftTests.swift in Sources */,
 				041EBBB31B8967C300E113B3 /* Dwifft+UIKit.swift in Sources */,
@@ -472,6 +481,7 @@
 				382DEF4B1F487519007A8FD2 /* Dwifft.swift in Sources */,
 				382DEF4C1F48751B007A8FD2 /* SectionedValues.swift in Sources */,
 				382DEF4D1F48751C007A8FD2 /* Dwifft+AppKit.swift in Sources */,
+				0440791A1FDF75FF00DDB952 /* AbstractDiffCalculator.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -480,6 +490,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				38E5B5EB1F7BDBC700F33285 /* DwifftTests-macOS.swift in Sources */,
+				04CEEB901FDF787D00C17562 /* AbstractDiffCalculator.swift in Sources */,
 				38E5B5EA1F7BDBBB00F33285 /* Dwifft+AppKit.swift in Sources */,
 				38E5B5D61F7BDB5400F33285 /* SectionedValues.swift in Sources */,
 				38E5B5D71F7BDB5400F33285 /* Dwifft.swift in Sources */,
@@ -493,6 +504,7 @@
 				94783CB71EFBA25300841579 /* Dwifft+UIKit.swift in Sources */,
 				94783CB61EFBA25300841579 /* SectionedValues.swift in Sources */,
 				94783CB51EFBA25300841579 /* Dwifft.swift in Sources */,
+				044079191FDF75FF00DDB952 /* AbstractDiffCalculator.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/Dwifft/AbstractDiffCalculator.swift b/Dwifft/AbstractDiffCalculator.swift
new file mode 100644
index 0000000..9db51ca
--- /dev/null
+++ b/Dwifft/AbstractDiffCalculator.swift
@@ -0,0 +1,93 @@
+//
+//  AbstractDiffCalculator.swift
+//  Dwifft
+//
+//  Created by Jack Flintermann on 12/11/17.
+//  Copyright © 2017 jflinter. All rights reserved.
+//
+
+import Foundation
+
+/// A parent class for all diff calculators. Don't use it directly.
+public class AbstractDiffCalculator<Section: Equatable, Value: Equatable> {
+    
+    internal init(initialSectionedValues: SectionedValues<Section, Value>) {
+        self._sectionedValues = initialSectionedValues
+    }
+    
+    /// The number of sections in the diff calculator. Return this inside
+    /// `numberOfSections(in: tableView)` or `numberOfSections(in: collectionView)`.
+    /// Don't implement that method any other way (see the docs for `numberOfObjects(inSection:)`
+    /// for more context).
+    public final func numberOfSections() -> Int {
+        return self.sectionedValues.sections.count
+    }
+    
+    /// The section at a given index. If you implement `tableView:titleForHeaderInSection` or
+    /// `collectionView:viewForSupplementaryElementOfKind:atIndexPath`, you can use this
+    /// method to get information about that section out of Dwifft.
+    ///
+    /// - Parameter forSection: the index of the section you care about.
+    /// - Returns: the Section at that index.
+    public final func value(forSection: Int) -> Section {
+        return self.sectionedValues[forSection].0
+    }
+    
+    
+    /// The, uh, number of objects in a given section. Use this to implement
+    /// `UITableViewDataSource.numberOfRowsInSection:` or `UICollectionViewDataSource.numberOfItemsInSection:`.
+    /// Seriously, don't implement that method any other way - there is some subtle timing stuff
+    /// around when this value should change in order to satisfy `UITableView`/`UICollectionView`'s internal
+    /// assertions, that Dwifft knows how to handle correctly. Read the source for
+    /// Dwifft+UIKit.swift if you don't believe me/want to learn more.
+    ///
+    /// - Parameter section: a section of your table/collection view
+    /// - Returns: the number of objects in that section.
+    public final func numberOfObjects(inSection section: Int) -> Int {
+        return self.sectionedValues[section].1.count
+    }
+    
+    
+    /// The value at a given index path. Use this to implement
+    /// `UITableViewDataSource.cellForRowAtIndexPath` or `UICollectionViewDataSource.cellForItemAtIndexPath`.
+    ///
+    /// - Parameter indexPath: the index path you are interested in
+    /// - Returns: the thing at that index path
+    public final func value(atIndexPath indexPath: IndexPath) -> Value {
+        #if os(iOS) || os(tvOS)
+            let row = indexPath.row
+        #endif
+        #if os(macOS)
+            let row = indexPath.item
+        #endif
+        return self.sectionedValues[indexPath.section].1[row]
+    }
+    
+    
+    /// Set this variable to automatically trigger the correct section/row/item insertion/deletions
+    /// on your table/collection view.
+    public final var sectionedValues: SectionedValues<Section, Value> {
+        get {
+            return _sectionedValues
+        }
+        set {
+            let oldSectionedValues = sectionedValues
+            let newSectionedValues = newValue
+            let diff = Dwifft.diff(lhs: oldSectionedValues, rhs: newSectionedValues)
+            if (diff.count > 0) {
+                self.processChanges(newState: newSectionedValues, diff: diff)
+            }
+        }
+    }
+    
+    internal static func buildSectionedValues(values: [Value], sectionIndex: Int) -> SectionedValues<Int, Value> {
+        let firstRows = (0..<sectionIndex).map { ($0, [Value]()) }
+        return SectionedValues(firstRows + [(sectionIndex, values)])
+    }
+    
+    // UITableView and UICollectionView both perform assertions on the *current* number of rows/items before performing any updates. As such, the `sectionedValues` property must be backed by an internal value that does not change until *after* `beginUpdates`/`performBatchUpdates` has been called.
+    internal final var _sectionedValues: SectionedValues<Section, Value>
+    internal func processChanges(newState: SectionedValues<Section, Value>, diff: [SectionedDiffStep<Section, Value>]){
+        fatalError("override me")
+    }
+}
diff --git a/Dwifft/Dwifft+AppKit.swift b/Dwifft/Dwifft+AppKit.swift
index 27f76c5..fde751b 100644
--- a/Dwifft/Dwifft+AppKit.swift
+++ b/Dwifft/Dwifft+AppKit.swift
@@ -9,169 +9,10 @@
 #if os(OSX)
 
 import Cocoa
-
-
-/// A parent class for all diff calculators. Don't use it directly.
-public class AbstractDiffCalculator<Section: Equatable, Value: Equatable> {
-
-    fileprivate init(initialSectionedValues: SectionedValues<Section, Value>) {
-        self._sectionedValues = initialSectionedValues
-    }
-
-    /// The number of sections in the diff calculator. Return this inside
-    /// `numberOfSections(in: tableView)` or `numberOfSections(in: collectionView)`.
-    /// Don't implement that method any other way (see the docs for `numberOfObjects(inSection:)`
-    /// for more context).
-    public final func numberOfSections() -> Int {
-        return self.sectionedValues.sections.count
-    }
-
-    /// The section at a given index. If you implement `tableView:titleForHeaderInSection` or
-    /// `collectionView:viewForSupplementaryElementOfKind:atIndexPath`, you can use this
-    /// method to get information about that section out of Dwifft.
-    ///
-    /// - Parameter forSection: the index of the section you care about.
-    /// - Returns: the Section at that index.
-    public final func value(forSection: Int) -> Section {
-        return self.sectionedValues[forSection].0
-    }
-
-
-    /// The, uh, number of objects in a given section. Use this to implement
-    /// `NSTableViewDataSource.numberOfRowsInSection:` or `NSCollectionViewDataSource.numberOfItemsInSection:`.
-    /// Seriously, don't implement that method any other way - there is some subtle timing stuff
-    /// around when this value should change in order to satisfy `NSTableView`/`NSCollectionView`'s internal
-    /// assertions, that Dwifft knows how to handle correctly. Read the source for
-    /// Dwifft+NSKit.swift if you don't believe me/want to learn more.
-    ///
-    /// - Parameter section: a section of your table/collection view
-    /// - Returns: the number of objects in that section.
-    public final func numberOfObjects(inSection section: Int) -> Int {
-        return self.sectionedValues[section].1.count
-    }
-
-
-    /// The value at a given index path. Use this to implement
-    /// `NSTableViewDataSource.objectValueForRow` or `NSCollectionViewDataSource.itemForRepresentedObjectAtIndexPath`.
-    ///
-    /// - Parameter indexPath: the index path you are interested in
-    /// - Returns: the thing at that index path
-    public final func value(atIndexPath indexPath: IndexPath) -> Value {
-        return self.sectionedValues[indexPath.section].1[indexPath.item]
-    }
-
-
-    /// Set this variable to automatically trigger the correct section/row/item insertion/deletions
-    /// on your table/collection view.
-    public final var sectionedValues: SectionedValues<Section, Value> {
-        get {
-            return _sectionedValues
-        }
-        set {
-            let oldSectionedValues = sectionedValues
-            let newSectionedValues = newValue
-            let diff = Dwifft.diff(lhs: oldSectionedValues, rhs: newSectionedValues)
-            if (diff.count > 0) {
-                self.processChanges(newState: newSectionedValues, diff: diff)
-            }
-        }
-    }
-
-    // NSTableView and NSCollectionView both perform assertions on the *current* number of rows/items before performing any updates. As such, the `sectionedValues` property must be backed by an internal value that does not change until *after* `beginUpdates`/`performBatchUpdates` has been called.
-    fileprivate final var _sectionedValues: SectionedValues<Section, Value>
-    fileprivate func processChanges(newState: SectionedValues<Section, Value>, diff: [SectionedDiffStep<Section, Value>]){
-        fatalError("override me")
-    }
-}
-
-/// NSTableView does not support sections so we hide this from users.
-private final class SectionedTableViewDiffCalculator<Section: Equatable, Value: Equatable>: AbstractDiffCalculator<Section, Value> {
-
-    /// The table view to be managed
-    public weak var tableView: NSTableView?
-
-    /// Initializes a new diff calculator.
-    ///
-    /// - Parameters:
-    ///   - tableView: the table view to be managed
-    ///   - initialSectionedValues: optional - if specified, these will be the initial contents of the diff calculator.
-    public init(tableView: NSTableView?, initialSectionedValues: SectionedValues<Section, Value> = SectionedValues()) {
-        self.tableView = tableView
-        super.init(initialSectionedValues: initialSectionedValues)
-    }
-
-    /// You can change insertion/deletion animations like this! Fade works well.
-    /// So does Top/Bottom. Left/Right/Middle are a little weird, but hey, do your thing.
-    public var insertionAnimation = NSTableView.AnimationOptions.slideUp, deletionAnimation = NSTableView.AnimationOptions.slideUp
-
-    override fileprivate func processChanges(newState: SectionedValues<Section, Value>, diff: [SectionedDiffStep<Section, Value>]) {
-        guard let tableView = self.tableView else { return }
-        tableView.beginUpdates()
-        self._sectionedValues = newState
-        for result in diff {
-            switch result {
-            case let .delete(_, row, _): tableView.removeRows(at: [row], withAnimation: self.deletionAnimation)
-            case let .insert(_, row, _): tableView.insertRows(at: [row], withAnimation: self.insertionAnimation)
-            default: fatalError("NSTableViews do not have sections")
-            }
-        }
-        tableView.endUpdates()
-    }
-}
-
-/// This class manages a `NSCollectionView`'s items and sections. It will make the necessary
-/// calls to the collection view to ensure that its UI is kept in sync with the contents
-/// of the `sectionedValues` property.
-public final class CollectionViewDiffCalculator<Section: Equatable, Value: Equatable> : AbstractDiffCalculator<Section, Value> {
-
-    /// The collection view to be managed.
-    public weak var collectionView: NSCollectionView?
-
-    /// Initializes a new diff calculator.
-    ///
-    /// - Parameters:
-    ///   - collectionView: the collection view to be managed.
-    ///   - initialSectionedValues: optional - if specified, these will be the initial contents of the diff calculator.
-    public init(collectionView: NSCollectionView?, initialSectionedValues: SectionedValues<Section, Value> = SectionedValues()) {
-        self.collectionView = collectionView
-        super.init(initialSectionedValues: initialSectionedValues)
-    }
-
-    override fileprivate func processChanges(newState: SectionedValues<Section, Value>, diff: [SectionedDiffStep<Section, Value>]) {
-        guard let collectionView = self.collectionView else { return }
-        collectionView.performBatchUpdates({
-            self._sectionedValues = newState
-            for result in diff {
-                switch result {
-                case let .delete(section, item, _): collectionView.deleteItems(at: [IndexPath(item: item, section: section)])
-                case let .insert(section, item, _): collectionView.insertItems(at: [IndexPath(item: item, section: section)])
-                case let .sectionDelete(section, _): collectionView.deleteSections(IndexSet(integer: section))
-                case let .sectionInsert(section, _):
-                  // NSCollectionViews don't seem to like it when inserting sections beyond numberOfSections
-                  // so adjust for that
-                  if section > collectionView.numberOfSections {
-                    collectionView.insertSections(IndexSet(integer: collectionView.numberOfSections))
-                  } else {
-                    collectionView.insertSections(IndexSet(integer: section))
-                  }
-                }
-            }
-        }, completionHandler: nil)
-    }
-}
-
-/// Let's say your data model consists of different sections containing different model types. Since
-/// `SectionedValues` requires a uniform type for all of its rows, this can be a clunky situation. You
-/// can address this in a couple of ways. The first is to define a custom enum that encompasses all of the
-/// things that *could* be in your data model - if section 1 has a bunch of `String`s, and section 2 has a bunch
-/// of `Int`s, define a `StringOrInt` enum that conforms to `Equatable`, and fill the `SectionedValues`
-/// that you use to drive your DiffCalculator up with those. Alternatively, if you are lazy, and your
-/// models all conform to `Hashable`, you can use a SimpleTableViewDiffCalculator instead.
-typealias SimpleCollectionViewDiffCalculator = CollectionViewDiffCalculator<AnyHashable, AnyHashable>
-
+    
 /// This class manages a `NSTableView`'s rows. It will make the necessary
 /// calls to the table view to ensure that its UI is kept in sync with the contents of the `rows` property.
-public final class TableViewDiffCalculator<Value: Equatable> {
+public final class TableViewDiffCalculator<Value: Equatable>: AbstractDiffCalculator<Int, Value> {
 
     /// The table view to be managed
     public weak var tableView: NSTableView?
@@ -181,26 +22,18 @@ public final class TableViewDiffCalculator<Value: Equatable> {
 
     /// You can change insertion/deletion animations like this! Fade works well.
     /// So does Top/Bottom. Left/Right/Middle are a little weird, but hey, do your thing.
-    public var insertionAnimation = NSTableView.AnimationOptions.slideUp {
-        didSet {
-            self.internalDiffCalculator.insertionAnimation = self.insertionAnimation 
-        }
-    }
+    public var insertionAnimation = NSTableView.AnimationOptions.slideUp
     
-    public var deletionAnimation = NSTableView.AnimationOptions.slideUp {
-        didSet {
-            self.internalDiffCalculator.deletionAnimation = self.deletionAnimation 
-        }
-    }
+    public var deletionAnimation = NSTableView.AnimationOptions.slideUp
 
     /// Set this variable to automatically trigger the correct row insertion/deletions
     /// on your table view.
     public var rows : [Value] {
         get {
-            return self.internalDiffCalculator.sectionedValues[self.sectionIndex].1
+            return self._sectionedValues[self.sectionIndex].1
         }
         set {
-            self.internalDiffCalculator.sectionedValues = TableViewDiffCalculator.buildSectionedValues(values: newValue, sectionIndex: self.sectionIndex)
+            self.sectionedValues = AbstractDiffCalculator<Int, Value>.buildSectionedValues(values: newValue, sectionIndex: self.sectionIndex)
         }
     }
 
@@ -212,17 +45,24 @@ public final class TableViewDiffCalculator<Value: Equatable> {
     ///   - sectionIndex: optional - all insertion/deletion calls will be made on this index.
     public init(tableView: NSTableView?, initialRows: [Value] = [], sectionIndex: Int = 0) {
         self.tableView = tableView
-        self.internalDiffCalculator = SectionedTableViewDiffCalculator(tableView: tableView, initialSectionedValues: TableViewDiffCalculator.buildSectionedValues(values: initialRows, sectionIndex: sectionIndex))
         self.sectionIndex = sectionIndex
+        super.init(initialSectionedValues: AbstractDiffCalculator<Int, Value>.buildSectionedValues(values: initialRows, sectionIndex: sectionIndex))
     }
-
-    fileprivate static func buildSectionedValues(values: [Value], sectionIndex: Int) -> SectionedValues<Int, Value> {
-        let firstRows = (0..<sectionIndex).map { ($0, [Value]()) }
-        return SectionedValues(firstRows + [(sectionIndex, values)])
+    
+    override internal func processChanges(newState: SectionedValues<Int, Value>, diff: [SectionedDiffStep<Int, Value>]) {
+        guard let tableView = self.tableView else { return }
+        tableView.beginUpdates()
+        self._sectionedValues = newState
+        for result in diff {
+            switch result {
+            case let .delete(_, row, _): tableView.removeRows(at: [row], withAnimation: self.deletionAnimation)
+            case let .insert(_, row, _): tableView.insertRows(at: [row], withAnimation: self.insertionAnimation)
+            default: fatalError("NSTableViews do not have sections")
+            }
+        }
+        tableView.endUpdates()
     }
 
-    private let internalDiffCalculator: SectionedTableViewDiffCalculator<Int, Value>
-
 }
 
 /// If your collection view only has a single section, or you only want to power a single section of it with Dwifft,
@@ -244,7 +84,7 @@ public final class SingleSectionCollectionViewDiffCalculator<Value: Equatable> {
             return self.internalDiffCalculator.sectionedValues[self.sectionIndex].1
         }
         set {
-            self.internalDiffCalculator.sectionedValues = TableViewDiffCalculator.buildSectionedValues(values: newValue, sectionIndex: self.sectionIndex)
+            self.internalDiffCalculator.sectionedValues = AbstractDiffCalculator<Int, Value>.buildSectionedValues(values: newValue, sectionIndex: self.sectionIndex)
         }
     }
 
@@ -256,12 +96,64 @@ public final class SingleSectionCollectionViewDiffCalculator<Value: Equatable> {
     ///   - sectionIndex: optional - all insertion/deletion calls will be made on this index.
     public init(collectionView: NSCollectionView?, initialItems: [Value] = [], sectionIndex: Int = 0) {
         self.collectionView = collectionView
-        self.internalDiffCalculator = CollectionViewDiffCalculator(collectionView: collectionView, initialSectionedValues: TableViewDiffCalculator.buildSectionedValues(values: initialItems, sectionIndex: sectionIndex))
+        let initialSectionedValues = AbstractDiffCalculator<Int, Value>.buildSectionedValues(values: initialItems, sectionIndex: sectionIndex)
+        self.internalDiffCalculator = CollectionViewDiffCalculator(collectionView: collectionView, initialSectionedValues: initialSectionedValues)
         self.sectionIndex = sectionIndex
     }
 
     private let internalDiffCalculator: CollectionViewDiffCalculator<Int, Value>
     
 }
+    
+
+/// This class manages a `NSCollectionView`'s items and sections. It will make the necessary
+/// calls to the collection view to ensure that its UI is kept in sync with the contents
+/// of the `sectionedValues` property.
+public final class CollectionViewDiffCalculator<Section: Equatable, Value: Equatable> : AbstractDiffCalculator<Section, Value> {
+    
+    /// The collection view to be managed.
+    public weak var collectionView: NSCollectionView?
+    
+    /// Initializes a new diff calculator.
+    ///
+    /// - Parameters:
+    ///   - collectionView: the collection view to be managed.
+    ///   - initialSectionedValues: optional - if specified, these will be the initial contents of the diff calculator.
+    public init(collectionView: NSCollectionView?, initialSectionedValues: SectionedValues<Section, Value> = SectionedValues()) {
+        self.collectionView = collectionView
+        super.init(initialSectionedValues: initialSectionedValues)
+    }
+    
+    override internal func processChanges(newState: SectionedValues<Section, Value>, diff: [SectionedDiffStep<Section, Value>]) {
+        guard let collectionView = self.collectionView else { return }
+        collectionView.performBatchUpdates({
+            self._sectionedValues = newState
+            for result in diff {
+                switch result {
+                case let .delete(section, item, _): collectionView.deleteItems(at: [IndexPath(item: item, section: section)])
+                case let .insert(section, item, _): collectionView.insertItems(at: [IndexPath(item: item, section: section)])
+                case let .sectionDelete(section, _): collectionView.deleteSections(IndexSet(integer: section))
+                case let .sectionInsert(section, _):
+                    // NSCollectionViews don't seem to like it when inserting sections beyond numberOfSections
+                    // so adjust for that
+                    if section > collectionView.numberOfSections {
+                        collectionView.insertSections(IndexSet(integer: collectionView.numberOfSections))
+                    } else {
+                        collectionView.insertSections(IndexSet(integer: section))
+                    }
+                }
+            }
+        }, completionHandler: nil)
+    }
+}
+
+/// Let's say your data model consists of different sections containing different model types. Since
+/// `SectionedValues` requires a uniform type for all of its rows, this can be a clunky situation. You
+/// can address this in a couple of ways. The first is to define a custom enum that encompasses all of the
+/// things that *could* be in your data model - if section 1 has a bunch of `String`s, and section 2 has a bunch
+/// of `Int`s, define a `StringOrInt` enum that conforms to `Equatable`, and fill the `SectionedValues`
+/// that you use to drive your DiffCalculator up with those. Alternatively, if you are lazy, and your
+/// models all conform to `Hashable`, you can use a SimpleTableViewDiffCalculator instead.
+typealias SimpleCollectionViewDiffCalculator = CollectionViewDiffCalculator<AnyHashable, AnyHashable>
 
 #endif
diff --git a/Dwifft/Dwifft+UIKit.swift b/Dwifft/Dwifft+UIKit.swift
index 8e29769..752c528 100644
--- a/Dwifft/Dwifft+UIKit.swift
+++ b/Dwifft/Dwifft+UIKit.swift
@@ -9,83 +9,7 @@
 #if os(iOS) || os(tvOS)
 
 import UIKit
-
-
-/// A parent class for all diff calculators. Don't use it directly.
-public class AbstractDiffCalculator<Section: Equatable, Value: Equatable> {
-
-    fileprivate init(initialSectionedValues: SectionedValues<Section, Value>) {
-        self._sectionedValues = initialSectionedValues
-    }
-
-    /// The number of sections in the diff calculator. Return this inside
-    /// `numberOfSections(in: tableView)` or `numberOfSections(in: collectionView)`.
-    /// Don't implement that method any other way (see the docs for `numberOfObjects(inSection:)`
-    /// for more context).
-    public final func numberOfSections() -> Int {
-        return self.sectionedValues.sections.count
-    }
-
-    /// The section at a given index. If you implement `tableView:titleForHeaderInSection` or
-    /// `collectionView:viewForSupplementaryElementOfKind:atIndexPath`, you can use this
-    /// method to get information about that section out of Dwifft.
-    ///
-    /// - Parameter forSection: the index of the section you care about.
-    /// - Returns: the Section at that index.
-    public final func value(forSection: Int) -> Section {
-        return self.sectionedValues[forSection].0
-    }
-
-
-    /// The, uh, number of objects in a given section. Use this to implement
-    /// `UITableViewDataSource.numberOfRowsInSection:` or `UICollectionViewDataSource.numberOfItemsInSection:`.
-    /// Seriously, don't implement that method any other way - there is some subtle timing stuff
-    /// around when this value should change in order to satisfy `UITableView`/`UICollectionView`'s internal
-    /// assertions, that Dwifft knows how to handle correctly. Read the source for
-    /// Dwifft+UIKit.swift if you don't believe me/want to learn more.
-    ///
-    /// - Parameter section: a section of your table/collection view
-    /// - Returns: the number of objects in that section.
-    public final func numberOfObjects(inSection section: Int) -> Int {
-        return self.sectionedValues[section].1.count
-    }
-
-
-    /// The value at a given index path. Use this to implement
-    /// `UITableViewDataSource.cellForRowAtIndexPath` or `UICollectionViewDataSource.cellForItemAtIndexPath`.
-    ///
-    /// - Parameter indexPath: the index path you are interested in
-    /// - Returns: the thing at that index path
-    public final func value(atIndexPath indexPath: IndexPath) -> Value {
-        return self.sectionedValues[indexPath.section].1[indexPath.row]
-    }
-
-
-    /// Set this variable to automatically trigger the correct section/row/item insertion/deletions
-    /// on your table/collection view.
-    public final var sectionedValues: SectionedValues<Section, Value> {
-        get {
-            return _sectionedValues
-        }
-        set {
-            let oldSectionedValues = sectionedValues
-            let newSectionedValues = newValue
-            let diff = Dwifft.diff(lhs: oldSectionedValues, rhs: newSectionedValues)
-            if (diff.count > 0) {
-                self.processChanges(newState: newSectionedValues, diff: diff)
-            }
-        }
-    }
-
-    // UITableView and UICollectionView both perform assertions on the *current* number of rows/items before performing any updates. As such, the `sectionedValues` property must be backed by an internal value that does not change until *after* `beginUpdates`/`performBatchUpdates` has been called.
-    fileprivate final var _sectionedValues: SectionedValues<Section, Value>
-    fileprivate func processChanges(newState: SectionedValues<Section, Value>, diff: [SectionedDiffStep<Section, Value>]){
-        fatalError("override me")
-    }
-}
-
-
-/// This class manages a `UITableView`'s rows and sections. It will make the necessary calls to
+    /// This class manages a `UITableView`'s rows and sections. It will make the necessary calls to
 /// the table view to ensure that its UI is kept in sync with the contents of the `sectionedValues` property.
 public final class TableViewDiffCalculator<Section: Equatable, Value: Equatable>: AbstractDiffCalculator<Section, Value> {
 
@@ -106,7 +30,7 @@ public final class TableViewDiffCalculator<Section: Equatable, Value: Equatable>
     /// So does Top/Bottom. Left/Right/Middle are a little weird, but hey, do your thing.
     public var insertionAnimation = UITableViewRowAnimation.automatic, deletionAnimation = UITableViewRowAnimation.automatic
 
-    override fileprivate func processChanges(newState: SectionedValues<Section, Value>, diff: [SectionedDiffStep<Section, Value>]) {
+    override internal func processChanges(newState: SectionedValues<Section, Value>, diff: [SectionedDiffStep<Section, Value>]) {
         guard let tableView = self.tableView else { return }
         tableView.beginUpdates()
         self._sectionedValues = newState
@@ -140,7 +64,7 @@ public final class CollectionViewDiffCalculator<Section: Equatable, Value: Equat
         super.init(initialSectionedValues: initialSectionedValues)
     }
 
-    override fileprivate func processChanges(newState: SectionedValues<Section, Value>, diff: [SectionedDiffStep<Section, Value>]) {
+    override internal func processChanges(newState: SectionedValues<Section, Value>, diff: [SectionedDiffStep<Section, Value>]) {
         guard let collectionView = self.collectionView else { return }
         collectionView.performBatchUpdates({
             self._sectionedValues = newState
@@ -201,7 +125,7 @@ public final class SingleSectionTableViewDiffCalculator<Value: Equatable> {
             return self.internalDiffCalculator.sectionedValues[self.sectionIndex].1
         }
         set {
-            self.internalDiffCalculator.sectionedValues = SingleSectionTableViewDiffCalculator.buildSectionedValues(values: newValue, sectionIndex: self.sectionIndex)
+            self.internalDiffCalculator.sectionedValues = AbstractDiffCalculator<Int, Value>.buildSectionedValues(values: newValue, sectionIndex: self.sectionIndex)
         }
     }
 
@@ -213,15 +137,11 @@ public final class SingleSectionTableViewDiffCalculator<Value: Equatable> {
     ///   - sectionIndex: optional - all insertion/deletion calls will be made on this index.
     public init(tableView: UITableView?, initialRows: [Value] = [], sectionIndex: Int = 0) {
         self.tableView = tableView
-        self.internalDiffCalculator = TableViewDiffCalculator(tableView: tableView, initialSectionedValues: SingleSectionTableViewDiffCalculator.buildSectionedValues(values: initialRows, sectionIndex: sectionIndex))
+        let initialSectionedValues = AbstractDiffCalculator<Int, Value>.buildSectionedValues(values: initialRows, sectionIndex: sectionIndex)
+        self.internalDiffCalculator = TableViewDiffCalculator(tableView: tableView, initialSectionedValues: initialSectionedValues)
         self.sectionIndex = sectionIndex
     }
 
-    fileprivate static func buildSectionedValues(values: [Value], sectionIndex: Int) -> SectionedValues<Int, Value> {
-        let firstRows = (0..<sectionIndex).map { ($0, [Value]()) }
-        return SectionedValues(firstRows + [(sectionIndex, values)])
-    }
-
     private let internalDiffCalculator: TableViewDiffCalculator<Int, Value>
 
 }
@@ -245,7 +165,7 @@ public final class SingleSectionCollectionViewDiffCalculator<Value: Equatable> {
             return self.internalDiffCalculator.sectionedValues[self.sectionIndex].1
         }
         set {
-            self.internalDiffCalculator.sectionedValues = SingleSectionTableViewDiffCalculator.buildSectionedValues(values: newValue, sectionIndex: self.sectionIndex)
+            self.internalDiffCalculator.sectionedValues = AbstractDiffCalculator<Int, Value>.buildSectionedValues(values: newValue, sectionIndex: self.sectionIndex)
         }
     }
 
@@ -257,7 +177,7 @@ public final class SingleSectionCollectionViewDiffCalculator<Value: Equatable> {
     ///   - sectionIndex: optional - all insertion/deletion calls will be made on this index.
     public init(collectionView: UICollectionView?, initialItems: [Value] = [], sectionIndex: Int = 0) {
         self.collectionView = collectionView
-        self.internalDiffCalculator = CollectionViewDiffCalculator(collectionView: collectionView, initialSectionedValues: SingleSectionTableViewDiffCalculator.buildSectionedValues(values: initialItems, sectionIndex: sectionIndex))
+        self.internalDiffCalculator = CollectionViewDiffCalculator(collectionView: collectionView, initialSectionedValues: AbstractDiffCalculator<Int, Value>.buildSectionedValues(values: initialItems, sectionIndex: sectionIndex))
         self.sectionIndex = sectionIndex
     }
 
diff --git a/DwifftTests/DwifftTests-macOS.swift b/DwifftTests/DwifftTests-macOS.swift
index 2267a5b..1709c55 100644
--- a/DwifftTests/DwifftTests-macOS.swift
+++ b/DwifftTests/DwifftTests-macOS.swift
@@ -327,6 +327,12 @@ class DwifftTests: XCTestCase {
 
   func testCollectionViewDiffCalculator() {
 
+    class TestCollectionViewItem: NSCollectionViewItem {
+        override func loadView() {
+            self.view = NSView()
+        }
+    }
+    
     class TestCollectionView: NSCollectionView {
 
       let insertionExpectations: [Int: XCTestExpectation]
@@ -375,7 +381,7 @@ class DwifftTests: XCTestCase {
         self.rows = rows
         super.init(nibName: nil, bundle: nil)
 
-        collectionView.register(NSCollectionViewItem.self, forItemWithIdentifier: self.itemIdentifier)
+        collectionView.register(TestCollectionViewItem.self, forItemWithIdentifier: self.itemIdentifier)
         collectionView.dataSource = self
       }
 

From 41d81bfe2e344a27fe5b4fba113b6034a78c7635 Mon Sep 17 00:00:00 2001
From: Jack Flintermann <jack@stripe.com>
Date: Mon, 11 Dec 2017 19:52:59 -0800
Subject: [PATCH 34/48] fix travis carthage

---
 .travis.yml       | 2 +-
 Cartfile.resolved | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index e5098ab..95ee467 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,7 +8,7 @@ matrix:
       before_install:
         export UUID=$(instruments -s | ruby -e "ARGF.each_line{ |ln| ln =~ /$NAME .* \[(.*)\]/; if \$1; puts(\$1); exit; end }");
       before_script:
-        carthage bootstrap --platform $PLATFORM --configuration Debug
+        carthage bootstrap --configuration Debug
       script:
         - set -o pipefail;
         - open -a "simulator" --args -CurrentDeviceUDID "$UUID"
diff --git a/Cartfile.resolved b/Cartfile.resolved
index 6b0a63e..36f72fa 100644
--- a/Cartfile.resolved
+++ b/Cartfile.resolved
@@ -1,2 +1,2 @@
-github "trill-lang/FileCheck" "0.0.4"
+github "trill-lang/FileCheck" "0.0.5"
 github "typelift/SwiftCheck" "0.9.1"

From d18a3961116071688ebba2f641a76043b6e41265 Mon Sep 17 00:00:00 2001
From: Dennis Oberhoff <dennis@obrhoff.de>
Date: Sat, 16 Dec 2017 10:17:00 +0100
Subject: [PATCH 35/48] Use Animator to animate NSCollectionView changes

---
 Dwifft/Dwifft+AppKit.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Dwifft/Dwifft+AppKit.swift b/Dwifft/Dwifft+AppKit.swift
index fde751b..a3831a1 100644
--- a/Dwifft/Dwifft+AppKit.swift
+++ b/Dwifft/Dwifft+AppKit.swift
@@ -126,7 +126,7 @@ public final class CollectionViewDiffCalculator<Section: Equatable, Value: Equat
     
     override internal func processChanges(newState: SectionedValues<Section, Value>, diff: [SectionedDiffStep<Section, Value>]) {
         guard let collectionView = self.collectionView else { return }
-        collectionView.performBatchUpdates({
+        collectionView.animator().performBatchUpdates({
             self._sectionedValues = newState
             for result in diff {
                 switch result {

From f67bb83c70702fb2238fbaf47afdbcc913fa5d64 Mon Sep 17 00:00:00 2001
From: Alex Leeds <alexleeds@hotmail.com>
Date: Fri, 14 Sep 2018 12:13:11 -0400
Subject: [PATCH 36/48] Update to Swift 4.2

---
 Dwifft.xcodeproj/project.pbxproj              | 48 ++++++++++---------
 .../xcshareddata/IDEWorkspaceChecks.plist     |  8 ++++
 .../xcschemes/Dwifft-macOS.xcscheme           |  4 +-
 .../xcschemes/Dwifft-tvOS.xcscheme            |  4 +-
 .../xcshareddata/xcschemes/Dwifft.xcscheme    |  8 ++--
 .../xcshareddata/IDEWorkspaceChecks.plist     |  8 ++++
 Dwifft/Dwifft+UIKit.swift                     |  6 +--
 .../DwifftExample-iOS/AppDelegate.swift       |  2 +-
 .../StuffCollectionViewController.swift       |  2 +-
 .../DwifftExample.xcodeproj/project.pbxproj   | 12 +++--
 .../StuffCollectionViewController.swift       |  8 ++--
 DwifftTests/DwifftTests-macOS.swift           |  4 +-
 DwifftTests/DwifftTests.swift                 | 14 +++---
 13 files changed, 72 insertions(+), 56 deletions(-)
 create mode 100644 Dwifft.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
 create mode 100644 Dwifft.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist

diff --git a/Dwifft.xcodeproj/project.pbxproj b/Dwifft.xcodeproj/project.pbxproj
index 6542a34..1229f53 100644
--- a/Dwifft.xcodeproj/project.pbxproj
+++ b/Dwifft.xcodeproj/project.pbxproj
@@ -359,20 +359,24 @@
 			attributes = {
 				LastSwiftMigration = 0700;
 				LastSwiftUpdateCheck = 0700;
-				LastUpgradeCheck = 0900;
+				LastUpgradeCheck = 1000;
 				ORGANIZATIONNAME = jflinter;
 				TargetAttributes = {
 					041EBB911B89679200E113B3 = {
 						CreatedOnToolsVersion = 6.4;
-						LastSwiftMigration = 0900;
+						LastSwiftMigration = 1000;
 					};
 					041EBB9C1B89679200E113B3 = {
 						CreatedOnToolsVersion = 6.4;
-						LastSwiftMigration = 0900;
+						LastSwiftMigration = 1000;
+						ProvisioningStyle = Automatic;
 					};
 					382DEF411F487395007A8FD2 = {
 						CreatedOnToolsVersion = 9.0;
-						LastSwiftMigration = 0900;
+						LastSwiftMigration = 1000;
+					};
+					38E5B5D21F7BDB5400F33285 = {
+						LastSwiftMigration = 1000;
 					};
 					94783CAC1EFBA21900841579 = {
 						CreatedOnToolsVersion = 9.0;
@@ -536,12 +540,14 @@
 				CLANG_WARN_BOOL_CONVERSION = YES;
 				CLANG_WARN_COMMA = YES;
 				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
 				CLANG_WARN_EMPTY_BODY = YES;
 				CLANG_WARN_ENUM_CONVERSION = YES;
 				CLANG_WARN_INFINITE_RECURSION = YES;
 				CLANG_WARN_INT_CONVERSION = YES;
 				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
 				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
 				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
@@ -575,7 +581,7 @@
 				ONLY_ACTIVE_ARCH = YES;
 				SDKROOT = iphoneos;
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
-				SWIFT_VERSION = 3.0.1;
+				SWIFT_VERSION = 4.2;
 				TARGETED_DEVICE_FAMILY = "1,2";
 				VERSIONING_SYSTEM = "apple-generic";
 				VERSION_INFO_PREFIX = "";
@@ -594,12 +600,14 @@
 				CLANG_WARN_BOOL_CONVERSION = YES;
 				CLANG_WARN_COMMA = YES;
 				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
 				CLANG_WARN_EMPTY_BODY = YES;
 				CLANG_WARN_ENUM_CONVERSION = YES;
 				CLANG_WARN_INFINITE_RECURSION = YES;
 				CLANG_WARN_INT_CONVERSION = YES;
 				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
 				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
 				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
@@ -624,7 +632,7 @@
 				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				SDKROOT = iphoneos;
-				SWIFT_VERSION = 3.0.1;
+				SWIFT_VERSION = 4.2;
 				TARGETED_DEVICE_FAMILY = "1,2";
 				VALIDATE_PRODUCT = YES;
 				VERSIONING_SYSTEM = "apple-generic";
@@ -646,7 +654,6 @@
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
-				SWIFT_VERSION = 4.0;
 			};
 			name = Debug;
 		};
@@ -665,13 +672,15 @@
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
-				SWIFT_VERSION = 4.0;
 			};
 			name = Release;
 		};
 		041EBBAC1B89679200E113B3 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_TEAM = "";
 				FRAMEWORK_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(PROJECT_DIR)/Carthage/Build/iOS",
@@ -684,13 +693,16 @@
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SWIFT_VERSION = 4.0;
+				PROVISIONING_PROFILE_SPECIFIER = "";
 			};
 			name = Debug;
 		};
 		041EBBAD1B89679200E113B3 /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_TEAM = "";
 				FRAMEWORK_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(PROJECT_DIR)/Carthage/Build/iOS",
@@ -699,8 +711,8 @@
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE_SPECIFIER = "";
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
-				SWIFT_VERSION = 4.0;
 			};
 			name = Release;
 		};
@@ -732,11 +744,10 @@
 				MACOSX_DEPLOYMENT_TARGET = 10.11;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.Dwifft-macOS";
 				PRODUCT_NAME = Dwifft;
-				SDKROOT = macosx10.13;
+				SDKROOT = macosx;
 				SKIP_INSTALL = YES;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
-				SWIFT_VERSION = 4.0;
 			};
 			name = Debug;
 		};
@@ -767,10 +778,9 @@
 				MACOSX_DEPLOYMENT_TARGET = 10.11;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.Dwifft-macOS";
 				PRODUCT_NAME = Dwifft;
-				SDKROOT = macosx10.13;
+				SDKROOT = macosx;
 				SKIP_INSTALL = YES;
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
-				SWIFT_VERSION = 4.0;
 			};
 			name = Release;
 		};
@@ -789,9 +799,7 @@
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SDKROOT = macosx10.13;
-				SWIFT_SWIFT3_OBJC_INFERENCE = Default;
-				SWIFT_VERSION = 4.0;
+				SDKROOT = macosx;
 			};
 			name = Debug;
 		};
@@ -806,10 +814,8 @@
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SDKROOT = macosx10.13;
+				SDKROOT = macosx;
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
-				SWIFT_SWIFT3_OBJC_INFERENCE = Default;
-				SWIFT_VERSION = 4.0;
 			};
 			name = Release;
 		};
@@ -841,7 +847,6 @@
 				SKIP_INSTALL = YES;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
 				SWIFT_SWIFT3_OBJC_INFERENCE = Default;
-				SWIFT_VERSION = 4.0;
 				TARGETED_DEVICE_FAMILY = 3;
 				TVOS_DEPLOYMENT_TARGET = 9.0;
 			};
@@ -874,7 +879,6 @@
 				SKIP_INSTALL = YES;
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
 				SWIFT_SWIFT3_OBJC_INFERENCE = Default;
-				SWIFT_VERSION = 4.0;
 				TARGETED_DEVICE_FAMILY = 3;
 				TVOS_DEPLOYMENT_TARGET = 9.0;
 			};
diff --git a/Dwifft.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Dwifft.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/Dwifft.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IDEDidComputeMac32BitWarning</key>
+	<true/>
+</dict>
+</plist>
diff --git a/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-macOS.xcscheme b/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-macOS.xcscheme
index f1717bf..6de1915 100644
--- a/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-macOS.xcscheme
+++ b/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-macOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "0900"
+   LastUpgradeVersion = "1000"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"
@@ -26,7 +26,6 @@
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      language = ""
       shouldUseLaunchSchemeArgsEnv = "YES">
       <Testables>
          <TestableReference
@@ -56,7 +55,6 @@
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      language = ""
       launchStyle = "0"
       useCustomWorkingDirectory = "NO"
       ignoresPersistentStateOnLaunch = "NO"
diff --git a/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-tvOS.xcscheme b/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-tvOS.xcscheme
index 2a096fe..a2dc107 100644
--- a/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-tvOS.xcscheme
+++ b/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft-tvOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "0900"
+   LastUpgradeVersion = "1000"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"
@@ -26,7 +26,6 @@
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      language = ""
       shouldUseLaunchSchemeArgsEnv = "YES">
       <Testables>
       </Testables>
@@ -37,7 +36,6 @@
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      language = ""
       launchStyle = "0"
       useCustomWorkingDirectory = "NO"
       ignoresPersistentStateOnLaunch = "NO"
diff --git a/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft.xcscheme b/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft.xcscheme
index 28b227b..62b4488 100644
--- a/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft.xcscheme
+++ b/Dwifft.xcodeproj/xcshareddata/xcschemes/Dwifft.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "0900"
+   LastUpgradeVersion = "1000"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"
@@ -40,9 +40,8 @@
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      language = ""
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      codeCoverageEnabled = "YES">
+      codeCoverageEnabled = "YES"
+      shouldUseLaunchSchemeArgsEnv = "YES">
       <Testables>
          <TestableReference
             skipped = "NO">
@@ -71,7 +70,6 @@
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      language = ""
       launchStyle = "0"
       useCustomWorkingDirectory = "NO"
       ignoresPersistentStateOnLaunch = "NO"
diff --git a/Dwifft.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Dwifft.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/Dwifft.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IDEDidComputeMac32BitWarning</key>
+	<true/>
+</dict>
+</plist>
diff --git a/Dwifft/Dwifft+UIKit.swift b/Dwifft/Dwifft+UIKit.swift
index 752c528..96c4beb 100644
--- a/Dwifft/Dwifft+UIKit.swift
+++ b/Dwifft/Dwifft+UIKit.swift
@@ -28,7 +28,7 @@ public final class TableViewDiffCalculator<Section: Equatable, Value: Equatable>
 
     /// You can change insertion/deletion animations like this! Fade works well.
     /// So does Top/Bottom. Left/Right/Middle are a little weird, but hey, do your thing.
-    public var insertionAnimation = UITableViewRowAnimation.automatic, deletionAnimation = UITableViewRowAnimation.automatic
+    public var insertionAnimation = UITableView.RowAnimation.automatic, deletionAnimation = UITableView.RowAnimation.automatic
 
     override internal func processChanges(newState: SectionedValues<Section, Value>, diff: [SectionedDiffStep<Section, Value>]) {
         guard let tableView = self.tableView else { return }
@@ -106,13 +106,13 @@ public final class SingleSectionTableViewDiffCalculator<Value: Equatable> {
 
     /// You can change insertion/deletion animations like this! Fade works well.
     /// So does Top/Bottom. Left/Right/Middle are a little weird, but hey, do your thing.
-    public var insertionAnimation = UITableViewRowAnimation.automatic {
+    public var insertionAnimation = UITableView.RowAnimation.automatic {
         didSet {
             self.internalDiffCalculator.insertionAnimation = self.insertionAnimation 
         }
     }
     
-    public var deletionAnimation = UITableViewRowAnimation.automatic {
+    public var deletionAnimation = UITableView.RowAnimation.automatic {
         didSet {
             self.internalDiffCalculator.deletionAnimation = self.deletionAnimation 
         }
diff --git a/DwifftExample/DwifftExample-iOS/AppDelegate.swift b/DwifftExample/DwifftExample-iOS/AppDelegate.swift
index 88ee714..b4f985e 100644
--- a/DwifftExample/DwifftExample-iOS/AppDelegate.swift
+++ b/DwifftExample/DwifftExample-iOS/AppDelegate.swift
@@ -14,7 +14,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
     var window: UIWindow?
 
 
-    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
+    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
         // Override point for customization after application launch.
         return true
     }
diff --git a/DwifftExample/DwifftExample-iOS/StuffCollectionViewController.swift b/DwifftExample/DwifftExample-iOS/StuffCollectionViewController.swift
index 14ef669..9f95696 100644
--- a/DwifftExample/DwifftExample-iOS/StuffCollectionViewController.swift
+++ b/DwifftExample/DwifftExample-iOS/StuffCollectionViewController.swift
@@ -80,7 +80,7 @@ final class StuffCollectionViewController: UICollectionViewController {
         collectionView.register(StuffCollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
         collectionView.register(
             StuffSectionHeaderView.self,
-            forSupplementaryViewOfKind: UICollectionElementKindSectionHeader,
+            forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader,
             withReuseIdentifier: headerReuseIdentifier
         )
     }
diff --git a/DwifftExample/DwifftExample.xcodeproj/project.pbxproj b/DwifftExample/DwifftExample.xcodeproj/project.pbxproj
index a1c0d9f..04cbdb4 100644
--- a/DwifftExample/DwifftExample.xcodeproj/project.pbxproj
+++ b/DwifftExample/DwifftExample.xcodeproj/project.pbxproj
@@ -296,7 +296,7 @@
 			isa = PBXProject;
 			attributes = {
 				LastSwiftUpdateCheck = 0910;
-				LastUpgradeCheck = 0900;
+				LastUpgradeCheck = 1000;
 				ORGANIZATIONNAME = jflinter;
 				TargetAttributes = {
 					045501F21B898C3200F5614D = {
@@ -481,12 +481,14 @@
 				CLANG_WARN_BOOL_CONVERSION = YES;
 				CLANG_WARN_COMMA = YES;
 				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
 				CLANG_WARN_EMPTY_BODY = YES;
 				CLANG_WARN_ENUM_CONVERSION = YES;
 				CLANG_WARN_INFINITE_RECURSION = YES;
 				CLANG_WARN_INT_CONVERSION = YES;
 				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
 				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
 				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
@@ -519,6 +521,7 @@
 				ONLY_ACTIVE_ARCH = YES;
 				SDKROOT = iphoneos;
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_VERSION = 4.2;
 			};
 			name = Debug;
 		};
@@ -534,12 +537,14 @@
 				CLANG_WARN_BOOL_CONVERSION = YES;
 				CLANG_WARN_COMMA = YES;
 				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
 				CLANG_WARN_EMPTY_BODY = YES;
 				CLANG_WARN_ENUM_CONVERSION = YES;
 				CLANG_WARN_INFINITE_RECURSION = YES;
 				CLANG_WARN_INT_CONVERSION = YES;
 				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
 				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
 				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
@@ -564,6 +569,7 @@
 				MTL_ENABLE_DEBUG_INFO = NO;
 				SDKROOT = iphoneos;
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+				SWIFT_VERSION = 4.2;
 				VALIDATE_PRODUCT = YES;
 			};
 			name = Release;
@@ -582,7 +588,6 @@
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_SWIFT3_OBJC_INFERENCE = Default;
-				SWIFT_VERSION = 4.0;
 			};
 			name = Debug;
 		};
@@ -600,7 +605,6 @@
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_SWIFT3_OBJC_INFERENCE = Default;
-				SWIFT_VERSION = 4.0;
 			};
 			name = Release;
 		};
@@ -627,7 +631,6 @@
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = macosx;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
-				SWIFT_VERSION = 4.0;
 			};
 			name = Debug;
 		};
@@ -652,7 +655,6 @@
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.DwifftExamples-macOS";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = macosx;
-				SWIFT_VERSION = 4.0;
 			};
 			name = Release;
 		};
diff --git a/DwifftExample/DwifftExamples-macOS/StuffCollectionViewController.swift b/DwifftExample/DwifftExamples-macOS/StuffCollectionViewController.swift
index 49ab94d..9b49030 100644
--- a/DwifftExample/DwifftExamples-macOS/StuffCollectionViewController.swift
+++ b/DwifftExample/DwifftExamples-macOS/StuffCollectionViewController.swift
@@ -20,11 +20,11 @@ final class StuffCollectionViewController: NSViewController {
     collectionView.dataSource = self
     diffCalculator = CollectionViewDiffCalculator(collectionView: collectionView, initialSectionedValues: stuff)
 
-    let itemNib = NSNib(nibNamed: NSNib.Name(rawValue: "StuffCollectionViewItem"), bundle: nil)
+    let itemNib = NSNib(nibNamed: NSNib.Name("StuffCollectionViewItem"), bundle: nil)
     collectionView.register(itemNib, forItemWithIdentifier: itemIdentifier)
 
-    let headerNib = NSNib(nibNamed: NSNib.Name(rawValue: "StuffCollectionHeaderView"), bundle: nil)
-    collectionView.register(headerNib, forSupplementaryViewOfKind: .sectionHeader, withIdentifier: headerIdentifier)
+    let headerNib = NSNib(nibNamed: NSNib.Name("StuffCollectionHeaderView"), bundle: nil)
+    collectionView.register(headerNib, forSupplementaryViewOfKind: NSCollectionView.elementKindSectionHeader, withIdentifier: headerIdentifier)
   }
 
   @IBAction func shuffle(_ sender: NSButton) {
@@ -44,7 +44,7 @@ extension StuffCollectionViewController: NSCollectionViewDataSource {
   func collectionView(_ collectionView: NSCollectionView,
                       viewForSupplementaryElementOfKind kind: NSCollectionView.SupplementaryElementKind,
                       at indexPath: IndexPath) -> NSView {
-    let header = collectionView.makeSupplementaryView(ofKind: .sectionHeader, withIdentifier: headerIdentifier, for: indexPath)
+    let header = collectionView.makeSupplementaryView(ofKind: NSCollectionView.elementKindSectionHeader, withIdentifier: headerIdentifier, for: indexPath)
 
     if let dc = diffCalculator, let stuffHeader = header as? StuffCollectionHeaderView {
       stuffHeader.title.stringValue = dc.value(forSection: indexPath.section)
diff --git a/DwifftTests/DwifftTests-macOS.swift b/DwifftTests/DwifftTests-macOS.swift
index 1709c55..a9ddd18 100644
--- a/DwifftTests/DwifftTests-macOS.swift
+++ b/DwifftTests/DwifftTests-macOS.swift
@@ -188,12 +188,12 @@ class DwifftTests: XCTestCase {
 
   func test2DBenchmark() {
     let n: Int = 70
-    let a: [(Int, [Int])] = (0...n).flatMap { (i: Int) -> (Int, [Int])? in
+    let a: [(Int, [Int])] = (0...n).compactMap { (i: Int) -> (Int, [Int])? in
       guard arc4random_uniform(2) == 0 else { return nil }
       let value: [Int] = (0...arc4random_uniform(UInt32(n))).map { _ in Int(arc4random_uniform(100)) }
       return (i, value)
     }
-    let b: [(Int, [Int])] = (0...n).flatMap { (i: Int) -> (Int, [Int])? in
+    let b: [(Int, [Int])] = (0...n).compactMap { (i: Int) -> (Int, [Int])? in
       guard arc4random_uniform(2) == 0 else { return nil }
       let value: [Int] = (0...arc4random_uniform(UInt32(n))).map { _ in Int(arc4random_uniform(100)) }
       return (i, value)
diff --git a/DwifftTests/DwifftTests.swift b/DwifftTests/DwifftTests.swift
index c758332..7f55220 100644
--- a/DwifftTests/DwifftTests.swift
+++ b/DwifftTests/DwifftTests.swift
@@ -211,12 +211,12 @@ class DwifftTests: XCTestCase {
 
     func test2DBenchmark() {
         let n: Int = 70
-        let a: [(Int, [Int])] = (0...n).flatMap { (i: Int) -> (Int, [Int])? in
+        let a: [(Int, [Int])] = (0...n).compactMap { (i: Int) -> (Int, [Int])? in
             guard arc4random_uniform(2) == 0 else { return nil }
             let value: [Int] = (0...arc4random_uniform(UInt32(n))).map { _ in Int(arc4random_uniform(100)) }
             return (i, value)
         }
-        let b: [(Int, [Int])] = (0...n).flatMap { (i: Int) -> (Int, [Int])? in
+        let b: [(Int, [Int])] = (0...n).compactMap { (i: Int) -> (Int, [Int])? in
             guard arc4random_uniform(2) == 0 else { return nil }
             let value: [Int] = (0...arc4random_uniform(UInt32(n))).map { _ in Int(arc4random_uniform(100)) }
             return (i, value)
@@ -272,22 +272,22 @@ class DwifftTests: XCTestCase {
             init(insertionExpectations: [Int: XCTestExpectation], deletionExpectations: [Int: XCTestExpectation]) {
                 self.insertionExpectations = insertionExpectations
                 self.deletionExpectations = deletionExpectations
-                super.init(frame: CGRect.zero, style: UITableViewStyle.plain)
+                super.init(frame: CGRect.zero, style: UITableView.Style.plain)
             }
 
             required init?(coder aDecoder: NSCoder) {
                 fatalError("not implemented")
             }
 
-            override func insertRows(at indexPaths: [IndexPath], with animation: UITableViewRowAnimation) {
-                XCTAssertEqual(animation, UITableViewRowAnimation.left, "incorrect insertion animation")
+            override func insertRows(at indexPaths: [IndexPath], with animation: UITableView.RowAnimation) {
+                XCTAssertEqual(animation, UITableView.RowAnimation.left, "incorrect insertion animation")
                 for indexPath in indexPaths {
                     self.insertionExpectations[(indexPath as NSIndexPath).row]!.fulfill()
                 }
             }
 
-            override func deleteRows(at indexPaths: [IndexPath], with animation: UITableViewRowAnimation) {
-                XCTAssertEqual(animation, UITableViewRowAnimation.right, "incorrect insertion animation")
+            override func deleteRows(at indexPaths: [IndexPath], with animation: UITableView.RowAnimation) {
+                XCTAssertEqual(animation, UITableView.RowAnimation.right, "incorrect insertion animation")
                 for indexPath in indexPaths {
                     self.deletionExpectations[(indexPath as NSIndexPath).row]!.fulfill()
                 }

From afa1dcbb028dfac1aa23d569bd127cb8cd96df92 Mon Sep 17 00:00:00 2001
From: Alex Leeds <alexleeds@hotmail.com>
Date: Fri, 14 Sep 2018 18:38:59 -0400
Subject: [PATCH 37/48] Update .travis.yml

---
 .travis.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.travis.yml b/.travis.yml
index 95ee467..a9b73ec 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,7 +2,7 @@ matrix:
   include:
     - os: osx
       language: objective-c
-      osx_image: xcode9
+      osx_image: xcode10
       env:
         - PLATFORM=iOS NAME='iPhone 8'
       before_install:

From 78117cd1f3b73242600e68ef0853dccf78a30cd8 Mon Sep 17 00:00:00 2001
From: Jack Flintermann <jflinter11@gmail.com>
Date: Fri, 14 Sep 2018 19:45:29 -0400
Subject: [PATCH 38/48] Update .travis.yml

---
 .travis.yml | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 95ee467..a791402 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,7 +2,7 @@ matrix:
   include:
     - os: osx
       language: objective-c
-      osx_image: xcode9
+      osx_image: xcode10
       env:
         - PLATFORM=iOS NAME='iPhone 8'
       before_install:
@@ -11,7 +11,6 @@ matrix:
         carthage bootstrap --configuration Debug
       script:
         - set -o pipefail;
-        - open -a "simulator" --args -CurrentDeviceUDID "$UUID"
         - xcodebuild -scheme Dwifft -destination "id=$UUID" test | xcpretty
         - xcodebuild -scheme Dwifft-macOS test | xcpretty
 

From 97826d17dc1bb2afa733a76ccad2773082577f03 Mon Sep 17 00:00:00 2001
From: Ilya Laryionau <larryonoff@gmail.com>
Date: Sun, 16 Sep 2018 14:00:51 +0300
Subject: [PATCH 39/48] Add  into

---
 Dwifft.podspec | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Dwifft.podspec b/Dwifft.podspec
index d7c02be..ff3070d 100644
--- a/Dwifft.podspec
+++ b/Dwifft.podspec
@@ -8,6 +8,8 @@ Pod::Spec.new do |s|
   s.author = 'Jack Flintermann'
   s.source = { git: 'https://github.com/jflinter/Dwifft.git', tag: s.version }
 
+  s.swift_version = '4.0'
+
   s.ios.deployment_target = '8.0'
   s.tvos.deployment_target = '9.0'
   s.osx.deployment_target = '10.11'

From 9c86830677bd423a8bb5e3cd386188250439291f Mon Sep 17 00:00:00 2001
From: jflinter <jflinter11@gmail.com>
Date: Tue, 18 Sep 2018 01:13:21 -0400
Subject: [PATCH 40/48] update to macos 10.13

---
 Dwifft.xcodeproj/project.pbxproj | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Dwifft.xcodeproj/project.pbxproj b/Dwifft.xcodeproj/project.pbxproj
index 6542a34..c469317 100644
--- a/Dwifft.xcodeproj/project.pbxproj
+++ b/Dwifft.xcodeproj/project.pbxproj
@@ -729,10 +729,10 @@
 				INFOPLIST_FILE = "Dwifft-macOS/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
-				MACOSX_DEPLOYMENT_TARGET = 10.11;
+				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.Dwifft-macOS";
 				PRODUCT_NAME = Dwifft;
-				SDKROOT = macosx10.13;
+				SDKROOT = macosx;
 				SKIP_INSTALL = YES;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -764,10 +764,10 @@
 				INFOPLIST_FILE = "Dwifft-macOS/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
-				MACOSX_DEPLOYMENT_TARGET = 10.11;
+				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.Dwifft-macOS";
 				PRODUCT_NAME = Dwifft;
-				SDKROOT = macosx10.13;
+				SDKROOT = macosx;
 				SKIP_INSTALL = YES;
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
 				SWIFT_VERSION = 4.0;

From 9406b784722fe6c61b215baf0a89991cd19fa3e2 Mon Sep 17 00:00:00 2001
From: jflinter <jflinter11@gmail.com>
Date: Tue, 18 Sep 2018 01:14:05 -0400
Subject: [PATCH 41/48] fix macos test target

---
 Dwifft.xcodeproj/project.pbxproj | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Dwifft.xcodeproj/project.pbxproj b/Dwifft.xcodeproj/project.pbxproj
index c469317..e496903 100644
--- a/Dwifft.xcodeproj/project.pbxproj
+++ b/Dwifft.xcodeproj/project.pbxproj
@@ -789,7 +789,7 @@
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SDKROOT = macosx10.13;
+				SDKROOT = macosx;
 				SWIFT_SWIFT3_OBJC_INFERENCE = Default;
 				SWIFT_VERSION = 4.0;
 			};
@@ -806,7 +806,7 @@
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SDKROOT = macosx10.13;
+				SDKROOT = macosx;
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
 				SWIFT_SWIFT3_OBJC_INFERENCE = Default;
 				SWIFT_VERSION = 4.0;

From 4aa4e1dd3af51ab6ac0ca5464f6655d9fd17683e Mon Sep 17 00:00:00 2001
From: jflinter <jflinter11@gmail.com>
Date: Tue, 18 Sep 2018 01:34:41 -0400
Subject: [PATCH 42/48] 0.9

---
 .swift-version | 2 +-
 Dwifft.podspec | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/.swift-version b/.swift-version
index 5186d07..bf77d54 100644
--- a/.swift-version
+++ b/.swift-version
@@ -1 +1 @@
-4.0
+4.2
diff --git a/Dwifft.podspec b/Dwifft.podspec
index ff3070d..3320fff 100644
--- a/Dwifft.podspec
+++ b/Dwifft.podspec
@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name = 'Dwifft'
-  s.version = '0.7'
+  s.version = '0.8'
   s.license = 'MIT'
   s.summary = 'Swift Diff'
   s.homepage = 'https://github.com/jflinter/Dwifft'
@@ -8,7 +8,7 @@ Pod::Spec.new do |s|
   s.author = 'Jack Flintermann'
   s.source = { git: 'https://github.com/jflinter/Dwifft.git', tag: s.version }
 
-  s.swift_version = '4.0'
+  s.swift_version = '4.2'
 
   s.ios.deployment_target = '8.0'
   s.tvos.deployment_target = '9.0'

From a006cd099779f835e1a357247a8de02031195a4e Mon Sep 17 00:00:00 2001
From: jflinter <jflinter11@gmail.com>
Date: Tue, 18 Sep 2018 01:35:51 -0400
Subject: [PATCH 43/48] 0.9

---
 Dwifft.podspec | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Dwifft.podspec b/Dwifft.podspec
index 3320fff..3b54116 100644
--- a/Dwifft.podspec
+++ b/Dwifft.podspec
@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name = 'Dwifft'
-  s.version = '0.8'
+  s.version = '0.9'
   s.license = 'MIT'
   s.summary = 'Swift Diff'
   s.homepage = 'https://github.com/jflinter/Dwifft'

From 361e65492a2a181d747836f7d8b0c5f3f9afd0e6 Mon Sep 17 00:00:00 2001
From: jflinter <jflinter11@gmail.com>
Date: Thu, 27 Sep 2018 22:56:49 -0400
Subject: [PATCH 44/48] fix osx deployment target

---
 Dwifft.xcodeproj/project.pbxproj | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Dwifft.xcodeproj/project.pbxproj b/Dwifft.xcodeproj/project.pbxproj
index 07d3310..1229f53 100644
--- a/Dwifft.xcodeproj/project.pbxproj
+++ b/Dwifft.xcodeproj/project.pbxproj
@@ -741,7 +741,7 @@
 				INFOPLIST_FILE = "Dwifft-macOS/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
-				MACOSX_DEPLOYMENT_TARGET = 10.13;
+				MACOSX_DEPLOYMENT_TARGET = 10.11;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.Dwifft-macOS";
 				PRODUCT_NAME = Dwifft;
 				SDKROOT = macosx;
@@ -775,7 +775,7 @@
 				INFOPLIST_FILE = "Dwifft-macOS/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
-				MACOSX_DEPLOYMENT_TARGET = 10.13;
+				MACOSX_DEPLOYMENT_TARGET = 10.11;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.jflinter.Dwifft-macOS";
 				PRODUCT_NAME = Dwifft;
 				SDKROOT = macosx;

From 9db2dc1bb6e9702b1e426d19bd3043c60eb2e1db Mon Sep 17 00:00:00 2001
From: Wolf McNally <wolf@wolfmcnally.com>
Date: Tue, 26 Mar 2019 16:44:39 -0700
Subject: [PATCH 45/48] Updated for Swift 5.

---
 Dwifft.podspec                                        | 2 +-
 Dwifft/Dwifft.swift                                   | 4 ++--
 Dwifft/SectionedValues.swift                          | 2 +-
 DwifftExample/DwifftExample.xcodeproj/project.pbxproj | 5 +++--
 4 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/Dwifft.podspec b/Dwifft.podspec
index 3b54116..fbab4a9 100644
--- a/Dwifft.podspec
+++ b/Dwifft.podspec
@@ -8,7 +8,7 @@ Pod::Spec.new do |s|
   s.author = 'Jack Flintermann'
   s.source = { git: 'https://github.com/jflinter/Dwifft.git', tag: s.version }
 
-  s.swift_version = '4.2'
+  s.swift_version = '5.0'
 
   s.ios.deployment_target = '8.0'
   s.tvos.deployment_target = '9.0'
diff --git a/Dwifft/Dwifft.swift b/Dwifft/Dwifft.swift
index 188d037..5efe041 100644
--- a/Dwifft/Dwifft.swift
+++ b/Dwifft/Dwifft.swift
@@ -309,13 +309,13 @@ public extension Array where Element: Equatable {
 
     /// Deprecated in favor of `Dwifft.diff`.
     @available(*, deprecated)
-    public func diff(_ other: [Element]) -> [DiffStep<Element>] {
+    func diff(_ other: [Element]) -> [DiffStep<Element>] {
         return Dwifft.diff(self, other)
     }
 
     /// Deprecated in favor of `Dwifft.apply`.
     @available(*, deprecated)
-    public func apply(_ diff: [DiffStep<Element>]) -> [Element] {
+    func apply(_ diff: [DiffStep<Element>]) -> [Element] {
         return Dwifft.apply(diff: diff, toArray: self)
     }
 
diff --git a/Dwifft/SectionedValues.swift b/Dwifft/SectionedValues.swift
index d87eac0..0556d90 100644
--- a/Dwifft/SectionedValues.swift
+++ b/Dwifft/SectionedValues.swift
@@ -72,7 +72,7 @@ public extension SectionedValues where Section: Hashable {
     ///     should be sorted before the second. Used to sort the sections in the returned `SectionedValues`.
     ///   - sortValues: A function that compares two values, and returns true if the first
     ///     should be sorted before the second. Used to sort the values in each section of the returned `SectionedValues`.
-    public init(
+    init(
         values: [Value],
         valueToSection: ((Value) -> Section),
         sortSections: ((Section, Section) -> Bool),
diff --git a/DwifftExample/DwifftExample.xcodeproj/project.pbxproj b/DwifftExample/DwifftExample.xcodeproj/project.pbxproj
index 04cbdb4..5ad9c23 100644
--- a/DwifftExample/DwifftExample.xcodeproj/project.pbxproj
+++ b/DwifftExample/DwifftExample.xcodeproj/project.pbxproj
@@ -314,6 +314,7 @@
 			developmentRegion = English;
 			hasScannedForEncodings = 0;
 			knownRegions = (
+				English,
 				en,
 				Base,
 			);
@@ -521,7 +522,7 @@
 				ONLY_ACTIVE_ARCH = YES;
 				SDKROOT = iphoneos;
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
-				SWIFT_VERSION = 4.2;
+				SWIFT_VERSION = 5.0;
 			};
 			name = Debug;
 		};
@@ -569,7 +570,7 @@
 				MTL_ENABLE_DEBUG_INFO = NO;
 				SDKROOT = iphoneos;
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
-				SWIFT_VERSION = 4.2;
+				SWIFT_VERSION = 5.0;
 				VALIDATE_PRODUCT = YES;
 			};
 			name = Release;

From 434d4d2a179a234ddd0645e193cf61cfb3791066 Mon Sep 17 00:00:00 2001
From: Wolf McNally <wolf@wolfmcnally.com>
Date: Sun, 9 Jun 2019 17:20:53 -0700
Subject: [PATCH 46/48] Updated .gitignore.

---
 .gitignore | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/.gitignore b/.gitignore
index 039109e..f14113e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,7 +4,6 @@
 ### Swift ###
 
 ## Build generated
-.build/
 build/
 DerivedData
 
@@ -29,4 +28,9 @@ xcuserdata
 *.hmap
 *.ipa
 
-Carthage/
\ No newline at end of file
+# Swift Package Manager
+.build
+.swiftpm
+Package.resolved
+
+Carthage/

From 4fcb62b0a96780606589beac00f74f317be5e0e0 Mon Sep 17 00:00:00 2001
From: Denys Telezhkin <strangervir@gmail.com>
Date: Tue, 9 Jul 2019 12:12:10 +0300
Subject: [PATCH 47/48] Add support fro Swift Package Manager in Xcode 11.

---
 Package.swift | 47 ++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 38 insertions(+), 9 deletions(-)

diff --git a/Package.swift b/Package.swift
index d0d14aa..2416eb0 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,14 +1,43 @@
+// swift-tools-version:5.1
+//
+//  Package.swift
+//  Dwifft
+//
+//  Created by Denys Telezhkin on 09.07.2019.
+//  Copyright © 2019 Denys Telezhkin. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
 import PackageDescription
 
 let package = Package(
     name: "Dwifft",
-    dependencies : [],
-    exclude: [
-        "Carthage",
-        "DwifftTests",
-        "DwifftExample",
-        "docs",
-        "Dwifft.xcworkspace",
-        "scripts",
-    ]
+    platforms: [
+        .iOS(.v8),
+        .tvOS(.v9),
+        .macOS(.v10_11)
+    ],
+    products: [
+        .library(name: "Dwifft", targets: ["Dwifft"])
+    ],
+    targets: [
+        .target(name: "Dwifft", path: "Dwifft")
+    ],
+    swiftLanguageVersions: [.v5]
 )

From f405eaa651be188c42b7cf0bf7b8a4df01179f59 Mon Sep 17 00:00:00 2001
From: Denys Telezhkin <strangervir@gmail.com>
Date: Wed, 10 Jul 2019 09:57:56 +0300
Subject: [PATCH 48/48] Remove copyright header.

---
 Package.swift | 23 -----------------------
 1 file changed, 23 deletions(-)

diff --git a/Package.swift b/Package.swift
index 2416eb0..df9459e 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,28 +1,5 @@
 // swift-tools-version:5.1
 //
-//  Package.swift
-//  Dwifft
-//
-//  Created by Denys Telezhkin on 09.07.2019.
-//  Copyright © 2019 Denys Telezhkin. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
 
 import PackageDescription