Build still broken. De-framework-ized FeedFinder.
This commit is contained in:
parent
3909e7d41f
commit
74e07521ea
@ -150,6 +150,12 @@
|
||||
84F2D5371FC22FCC00998D64 /* PseudoFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F2D5351FC22FCB00998D64 /* PseudoFeed.swift */; };
|
||||
84F2D5381FC22FCC00998D64 /* TodayFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F2D5361FC22FCB00998D64 /* TodayFeedDelegate.swift */; };
|
||||
84F2D53A1FC2308B00998D64 /* UnreadFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F2D5391FC2308B00998D64 /* UnreadFeed.swift */; };
|
||||
84F3EE1620DEC97E003FADEB /* FeedFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F3EE0620DEC97E003FADEB /* FeedFinder.swift */; };
|
||||
84F3EE1720DEC97E003FADEB /* FeedFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F3EE0620DEC97E003FADEB /* FeedFinder.swift */; };
|
||||
84F3EE1820DEC97E003FADEB /* FeedSpecifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F3EE0720DEC97E003FADEB /* FeedSpecifier.swift */; };
|
||||
84F3EE1920DEC97E003FADEB /* FeedSpecifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F3EE0720DEC97E003FADEB /* FeedSpecifier.swift */; };
|
||||
84F3EE1A20DEC97E003FADEB /* HTMLFeedFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F3EE0820DEC97E003FADEB /* HTMLFeedFinder.swift */; };
|
||||
84F3EE1B20DEC97E003FADEB /* HTMLFeedFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F3EE0820DEC97E003FADEB /* HTMLFeedFinder.swift */; };
|
||||
84FB9A2F1EDCD6C4003D53B9 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */; };
|
||||
84FB9A301EDCD6C4003D53B9 /* Sparkle.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
84FF69B11FC3793300DC198E /* FaviconURLFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FF69B01FC3793300DC198E /* FaviconURLFinder.swift */; };
|
||||
@ -634,6 +640,9 @@
|
||||
84F2D5351FC22FCB00998D64 /* PseudoFeed.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PseudoFeed.swift; sourceTree = "<group>"; };
|
||||
84F2D5361FC22FCB00998D64 /* TodayFeedDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TodayFeedDelegate.swift; sourceTree = "<group>"; };
|
||||
84F2D5391FC2308B00998D64 /* UnreadFeed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnreadFeed.swift; sourceTree = "<group>"; };
|
||||
84F3EE0620DEC97E003FADEB /* FeedFinder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedFinder.swift; sourceTree = "<group>"; };
|
||||
84F3EE0720DEC97E003FADEB /* FeedSpecifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedSpecifier.swift; sourceTree = "<group>"; };
|
||||
84F3EE0820DEC97E003FADEB /* HTMLFeedFinder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTMLFeedFinder.swift; sourceTree = "<group>"; };
|
||||
84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = Frameworks/Vendor/Sparkle.framework; sourceTree = SOURCE_ROOT; };
|
||||
84FF69B01FC3793300DC198E /* FaviconURLFinder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconURLFinder.swift; sourceTree = "<group>"; };
|
||||
D553737C20186C1F006D8857 /* Article+Scriptability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Article+Scriptability.swift"; sourceTree = "<group>"; };
|
||||
@ -1075,6 +1084,7 @@
|
||||
8444C9011FED81880051386C /* Exporters */,
|
||||
84F2D5341FC22FCB00998D64 /* SmartFeeds */,
|
||||
849A97561ED9EB0D007D329B /* Data */,
|
||||
84F3EE0420DEC97E003FADEB /* FeedFinder */,
|
||||
8426119C1FCB6ED40086A189 /* HTMLMetadata */,
|
||||
848F6AE31FC29CFA002D422E /* Favicons */,
|
||||
845213211FCA5B10003B6E93 /* Images */,
|
||||
@ -1266,6 +1276,17 @@
|
||||
path = Evergreen/SmartFeeds;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84F3EE0420DEC97E003FADEB /* FeedFinder */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84F3EE0620DEC97E003FADEB /* FeedFinder.swift */,
|
||||
84F3EE0720DEC97E003FADEB /* FeedSpecifier.swift */,
|
||||
84F3EE0820DEC97E003FADEB /* HTMLFeedFinder.swift */,
|
||||
);
|
||||
name = FeedFinder;
|
||||
path = Evergreen/FeedFinder;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84FB9A2C1EDCD6A4003D53B9 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -1786,6 +1807,9 @@
|
||||
840D61832029031C009BC708 /* DetailViewController.swift in Sources */,
|
||||
840D61812029031C009BC708 /* MasterViewController.swift in Sources */,
|
||||
840D617F2029031C009BC708 /* AppDelegate.swift in Sources */,
|
||||
84F3EE1720DEC97E003FADEB /* FeedFinder.swift in Sources */,
|
||||
84F3EE1B20DEC97E003FADEB /* HTMLFeedFinder.swift in Sources */,
|
||||
84F3EE1920DEC97E003FADEB /* FeedSpecifier.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -1866,8 +1890,10 @@
|
||||
84F204CE1FAACB660076E152 /* FeedListViewController.swift in Sources */,
|
||||
84AD1EBC2032AF5C00BC20B7 /* SidebarOutlineDataSource.swift in Sources */,
|
||||
845A29241FC9255E007B49E3 /* SidebarCellAppearance.swift in Sources */,
|
||||
84F3EE1620DEC97E003FADEB /* FeedFinder.swift in Sources */,
|
||||
845EE7B11FC2366500854A1F /* StarredFeedDelegate.swift in Sources */,
|
||||
848F6AE51FC29CFB002D422E /* FaviconDownloader.swift in Sources */,
|
||||
84F3EE1820DEC97E003FADEB /* FeedSpecifier.swift in Sources */,
|
||||
849EE72120391F560082A1EA /* MainWindowSharingServicePickerDelegate.swift in Sources */,
|
||||
849A97981ED9EFAA007D329B /* Node-Extensions.swift in Sources */,
|
||||
849EE70F203919360082A1EA /* AppImages.swift in Sources */,
|
||||
@ -1889,6 +1915,7 @@
|
||||
841ABA6020145EC100980E11 /* BuiltinSmartFeedInspectorViewController.swift in Sources */,
|
||||
D5E4CC54202C1361009B4FFC /* AppDelegate+Scriptability.swift in Sources */,
|
||||
D5F4EDB5200744A700B9E363 /* ScriptingObject.swift in Sources */,
|
||||
84F3EE1A20DEC97E003FADEB /* HTMLFeedFinder.swift in Sources */,
|
||||
D5F4EDB920074D7C00B9E363 /* Folder+Scriptability.swift in Sources */,
|
||||
842611A01FCB72600086A189 /* FeaturedImageDownloader.swift in Sources */,
|
||||
849A97781ED9EC04007D329B /* TimelineCellLayout.swift in Sources */,
|
||||
|
@ -1,384 +0,0 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
84B06FEF1ED3808F00F0B54B /* RSWeb.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84B06FEE1ED3808F00F0B54B /* RSWeb.framework */; };
|
||||
84B06FF31ED3812600F0B54B /* RSCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84B06FF21ED3812600F0B54B /* RSCore.framework */; };
|
||||
84BAAE2A1C8E6B3B009F5239 /* FeedFinder.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84BAAE1F1C8E6B3B009F5239 /* FeedFinder.framework */; };
|
||||
84F466571F03523100225386 /* RSParser.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84F466561F03523100225386 /* RSParser.framework */; };
|
||||
84FE13C220DC3D960029AB7B /* FeedFinder.h in Headers */ = {isa = PBXBuildFile; fileRef = 84FE13C120DC3D960029AB7B /* FeedFinder.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
84FE13C420DC3DA70029AB7B /* FeedFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FE13C320DC3DA70029AB7B /* FeedFinder.swift */; };
|
||||
84FE13C620DC3DAE0029AB7B /* FeedSpecifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FE13C520DC3DAE0029AB7B /* FeedSpecifier.swift */; };
|
||||
84FE13C820DC3DB50029AB7B /* HTMLFeedFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FE13C720DC3DB50029AB7B /* HTMLFeedFinder.swift */; };
|
||||
84FE13D420DC3DDF0029AB7B /* FeedFinderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 84FE13CB20DC3DDF0029AB7B /* FeedFinderTests.m */; };
|
||||
84FE13D520DC3DDF0029AB7B /* indiestack.html in Resources */ = {isa = PBXBuildFile; fileRef = 84FE13CD20DC3DDF0029AB7B /* indiestack.html */; };
|
||||
84FE13D620DC3DDF0029AB7B /* sixcolors.html in Resources */ = {isa = PBXBuildFile; fileRef = 84FE13CE20DC3DDF0029AB7B /* sixcolors.html */; };
|
||||
84FE13D720DC3DDF0029AB7B /* DaringFireball.html in Resources */ = {isa = PBXBuildFile; fileRef = 84FE13CF20DC3DDF0029AB7B /* DaringFireball.html */; };
|
||||
84FE13D820DC3DDF0029AB7B /* inessential.html in Resources */ = {isa = PBXBuildFile; fileRef = 84FE13D020DC3DDF0029AB7B /* inessential.html */; };
|
||||
84FE13D920DC3DDF0029AB7B /* furbo.html in Resources */ = {isa = PBXBuildFile; fileRef = 84FE13D120DC3DDF0029AB7B /* furbo.html */; };
|
||||
84FE13DA20DC3DDF0029AB7B /* HTMLFeedFinderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FE13D220DC3DDF0029AB7B /* HTMLFeedFinderTests.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
84BAAE2B1C8E6B3B009F5239 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 84BAAE161C8E6B3B009F5239 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 84BAAE1E1C8E6B3B009F5239;
|
||||
remoteInfo = RSFeedFinder;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
84B06FEE1ED3808F00F0B54B /* RSWeb.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RSWeb.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Evergreen-edcdqyvewhytnmcfaiesnqiqeynn/Build/Products/Debug/RSWeb.framework"; sourceTree = "<group>"; };
|
||||
84B06FF01ED380A700F0B54B /* RSXML.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RSXML.framework; path = ../RSXML/build/Debug/RSXML.framework; sourceTree = "<group>"; };
|
||||
84B06FF21ED3812600F0B54B /* RSCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RSCore.framework; path = ../RSCore/build/Debug/RSCore.framework; sourceTree = "<group>"; };
|
||||
84BAAE1F1C8E6B3B009F5239 /* FeedFinder.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FeedFinder.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
84BAAE291C8E6B3B009F5239 /* FeedFinderTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FeedFinderTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
84E697E51C8E6C10009C585A /* RSCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RSCore.framework; path = ../RSCore/build/Debug/RSCore.framework; sourceTree = "<group>"; };
|
||||
84E697E71C8E6C16009C585A /* RSXML.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RSXML.framework; path = ../RSXML/build/Debug/RSXML.framework; sourceTree = "<group>"; };
|
||||
84E697E91C8E6C20009C585A /* RSWeb.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RSWeb.framework; path = ../RSWeb/build/Debug/RSWeb.framework; sourceTree = "<group>"; };
|
||||
84F466561F03523100225386 /* RSParser.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RSParser.framework; path = ../RSParser/build/Debug/RSParser.framework; sourceTree = "<group>"; };
|
||||
84FE13C120DC3D960029AB7B /* FeedFinder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FeedFinder.h; path = FeedFinder/FeedFinder.h; sourceTree = "<group>"; };
|
||||
84FE13C320DC3DA70029AB7B /* FeedFinder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedFinder.swift; path = FeedFinder/FeedFinder.swift; sourceTree = "<group>"; };
|
||||
84FE13C520DC3DAE0029AB7B /* FeedSpecifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedSpecifier.swift; path = FeedFinder/FeedSpecifier.swift; sourceTree = "<group>"; };
|
||||
84FE13C720DC3DB50029AB7B /* HTMLFeedFinder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HTMLFeedFinder.swift; path = FeedFinder/HTMLFeedFinder.swift; sourceTree = "<group>"; };
|
||||
84FE13C920DC3DCA0029AB7B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = FeedFinder/Info.plist; sourceTree = "<group>"; };
|
||||
84FE13CB20DC3DDF0029AB7B /* FeedFinderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FeedFinderTests.m; sourceTree = "<group>"; };
|
||||
84FE13CD20DC3DDF0029AB7B /* indiestack.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = indiestack.html; sourceTree = "<group>"; };
|
||||
84FE13CE20DC3DDF0029AB7B /* sixcolors.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = sixcolors.html; sourceTree = "<group>"; };
|
||||
84FE13CF20DC3DDF0029AB7B /* DaringFireball.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = DaringFireball.html; sourceTree = "<group>"; };
|
||||
84FE13D020DC3DDF0029AB7B /* inessential.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = inessential.html; sourceTree = "<group>"; };
|
||||
84FE13D120DC3DDF0029AB7B /* furbo.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = furbo.html; sourceTree = "<group>"; };
|
||||
84FE13D220DC3DDF0029AB7B /* HTMLFeedFinderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTMLFeedFinderTests.swift; sourceTree = "<group>"; };
|
||||
84FE13D320DC3DDF0029AB7B /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
84FE13DC20DC3E4E0029AB7B /* FeedFinder_project_debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = FeedFinder_project_debug.xcconfig; sourceTree = "<group>"; };
|
||||
D511EF0920242E7A00712EC3 /* FeedFinder_project.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = FeedFinder_project.xcconfig; sourceTree = "<group>"; };
|
||||
D511EF0A20242E7A00712EC3 /* FeedFinder_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = FeedFinder_target.xcconfig; sourceTree = "<group>"; };
|
||||
D511EF0B20242E7A00712EC3 /* FeedFinder_project_release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = FeedFinder_project_release.xcconfig; sourceTree = "<group>"; };
|
||||
D511EF0C20242E7A00712EC3 /* FeedFinderTests_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = FeedFinderTests_target.xcconfig; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
84BAAE1B1C8E6B3B009F5239 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
84F466571F03523100225386 /* RSParser.framework in Frameworks */,
|
||||
84B06FF31ED3812600F0B54B /* RSCore.framework in Frameworks */,
|
||||
84B06FEF1ED3808F00F0B54B /* RSWeb.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
84BAAE261C8E6B3B009F5239 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
84BAAE2A1C8E6B3B009F5239 /* FeedFinder.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
84B06FED1ED3808E00F0B54B /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84F466561F03523100225386 /* RSParser.framework */,
|
||||
84B06FF21ED3812600F0B54B /* RSCore.framework */,
|
||||
84B06FF01ED380A700F0B54B /* RSXML.framework */,
|
||||
84B06FEE1ED3808F00F0B54B /* RSWeb.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84BAAE151C8E6B3B009F5239 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84FE13C120DC3D960029AB7B /* FeedFinder.h */,
|
||||
84FE13C320DC3DA70029AB7B /* FeedFinder.swift */,
|
||||
84FE13C520DC3DAE0029AB7B /* FeedSpecifier.swift */,
|
||||
84FE13C720DC3DB50029AB7B /* HTMLFeedFinder.swift */,
|
||||
84FE13C920DC3DCA0029AB7B /* Info.plist */,
|
||||
84E697E91C8E6C20009C585A /* RSWeb.framework */,
|
||||
84E697E71C8E6C16009C585A /* RSXML.framework */,
|
||||
84E697E51C8E6C10009C585A /* RSCore.framework */,
|
||||
84FE13CA20DC3DDF0029AB7B /* FeedFinderTests */,
|
||||
84BAAE201C8E6B3B009F5239 /* Products */,
|
||||
84B06FED1ED3808E00F0B54B /* Frameworks */,
|
||||
D511EF0820242E7A00712EC3 /* xcconfig */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84BAAE201C8E6B3B009F5239 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84BAAE1F1C8E6B3B009F5239 /* FeedFinder.framework */,
|
||||
84BAAE291C8E6B3B009F5239 /* FeedFinderTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84FE13CA20DC3DDF0029AB7B /* FeedFinderTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84FE13CB20DC3DDF0029AB7B /* FeedFinderTests.m */,
|
||||
84FE13CC20DC3DDF0029AB7B /* Resources */,
|
||||
84FE13D220DC3DDF0029AB7B /* HTMLFeedFinderTests.swift */,
|
||||
84FE13D320DC3DDF0029AB7B /* Info.plist */,
|
||||
);
|
||||
path = FeedFinderTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84FE13CC20DC3DDF0029AB7B /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84FE13CD20DC3DDF0029AB7B /* indiestack.html */,
|
||||
84FE13CE20DC3DDF0029AB7B /* sixcolors.html */,
|
||||
84FE13CF20DC3DDF0029AB7B /* DaringFireball.html */,
|
||||
84FE13D020DC3DDF0029AB7B /* inessential.html */,
|
||||
84FE13D120DC3DDF0029AB7B /* furbo.html */,
|
||||
);
|
||||
path = Resources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D511EF0820242E7A00712EC3 /* xcconfig */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D511EF0920242E7A00712EC3 /* FeedFinder_project.xcconfig */,
|
||||
84FE13DC20DC3E4E0029AB7B /* FeedFinder_project_debug.xcconfig */,
|
||||
D511EF0B20242E7A00712EC3 /* FeedFinder_project_release.xcconfig */,
|
||||
D511EF0A20242E7A00712EC3 /* FeedFinder_target.xcconfig */,
|
||||
D511EF0C20242E7A00712EC3 /* FeedFinderTests_target.xcconfig */,
|
||||
);
|
||||
path = xcconfig;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
84BAAE1C1C8E6B3B009F5239 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
84FE13C220DC3D960029AB7B /* FeedFinder.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
84BAAE1E1C8E6B3B009F5239 /* FeedFinder */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 84BAAE331C8E6B3B009F5239 /* Build configuration list for PBXNativeTarget "FeedFinder" */;
|
||||
buildPhases = (
|
||||
84BAAE1A1C8E6B3B009F5239 /* Sources */,
|
||||
84BAAE1B1C8E6B3B009F5239 /* Frameworks */,
|
||||
84BAAE1C1C8E6B3B009F5239 /* Headers */,
|
||||
84BAAE1D1C8E6B3B009F5239 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = FeedFinder;
|
||||
productName = RSFeedFinder;
|
||||
productReference = 84BAAE1F1C8E6B3B009F5239 /* FeedFinder.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
84BAAE281C8E6B3B009F5239 /* FeedFinderTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 84BAAE361C8E6B3B009F5239 /* Build configuration list for PBXNativeTarget "FeedFinderTests" */;
|
||||
buildPhases = (
|
||||
84BAAE251C8E6B3B009F5239 /* Sources */,
|
||||
84BAAE261C8E6B3B009F5239 /* Frameworks */,
|
||||
84BAAE271C8E6B3B009F5239 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
84BAAE2C1C8E6B3B009F5239 /* PBXTargetDependency */,
|
||||
);
|
||||
name = FeedFinderTests;
|
||||
productName = RSFeedFinderTests;
|
||||
productReference = 84BAAE291C8E6B3B009F5239 /* FeedFinderTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
84BAAE161C8E6B3B009F5239 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0930;
|
||||
ORGANIZATIONNAME = "Ranchero Software";
|
||||
TargetAttributes = {
|
||||
84BAAE1E1C8E6B3B009F5239 = {
|
||||
CreatedOnToolsVersion = 7.2.1;
|
||||
LastSwiftMigration = 0800;
|
||||
};
|
||||
84BAAE281C8E6B3B009F5239 = {
|
||||
CreatedOnToolsVersion = 7.2.1;
|
||||
LastSwiftMigration = 0800;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 84BAAE191C8E6B3B009F5239 /* Build configuration list for PBXProject "FeedFinder" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
);
|
||||
mainGroup = 84BAAE151C8E6B3B009F5239;
|
||||
productRefGroup = 84BAAE201C8E6B3B009F5239 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
84BAAE1E1C8E6B3B009F5239 /* FeedFinder */,
|
||||
84BAAE281C8E6B3B009F5239 /* FeedFinderTests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
84BAAE1D1C8E6B3B009F5239 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
84BAAE271C8E6B3B009F5239 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
84FE13D820DC3DDF0029AB7B /* inessential.html in Resources */,
|
||||
84FE13D920DC3DDF0029AB7B /* furbo.html in Resources */,
|
||||
84FE13D720DC3DDF0029AB7B /* DaringFireball.html in Resources */,
|
||||
84FE13D520DC3DDF0029AB7B /* indiestack.html in Resources */,
|
||||
84FE13D620DC3DDF0029AB7B /* sixcolors.html in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
84BAAE1A1C8E6B3B009F5239 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
84FE13C420DC3DA70029AB7B /* FeedFinder.swift in Sources */,
|
||||
84FE13C820DC3DB50029AB7B /* HTMLFeedFinder.swift in Sources */,
|
||||
84FE13C620DC3DAE0029AB7B /* FeedSpecifier.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
84BAAE251C8E6B3B009F5239 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
84FE13DA20DC3DDF0029AB7B /* HTMLFeedFinderTests.swift in Sources */,
|
||||
84FE13D420DC3DDF0029AB7B /* FeedFinderTests.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
84BAAE2C1C8E6B3B009F5239 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 84BAAE1E1C8E6B3B009F5239 /* FeedFinder */;
|
||||
targetProxy = 84BAAE2B1C8E6B3B009F5239 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
84BAAE311C8E6B3B009F5239 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
84BAAE321C8E6B3B009F5239 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D511EF0B20242E7A00712EC3 /* FeedFinder_project_release.xcconfig */;
|
||||
buildSettings = {
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
84BAAE341C8E6B3B009F5239 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D511EF0A20242E7A00712EC3 /* FeedFinder_target.xcconfig */;
|
||||
buildSettings = {
|
||||
INFOPLIST_FILE = FeedFinder/Info.plist;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.ranchero.FeedFinder;
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
84BAAE351C8E6B3B009F5239 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D511EF0A20242E7A00712EC3 /* FeedFinder_target.xcconfig */;
|
||||
buildSettings = {
|
||||
INFOPLIST_FILE = FeedFinder/Info.plist;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.ranchero.FeedFinder;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
84BAAE371C8E6B3B009F5239 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D511EF0C20242E7A00712EC3 /* FeedFinderTests_target.xcconfig */;
|
||||
buildSettings = {
|
||||
INFOPLIST_FILE = FeedFinderTests/Info.plist;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
84BAAE381C8E6B3B009F5239 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D511EF0C20242E7A00712EC3 /* FeedFinderTests_target.xcconfig */;
|
||||
buildSettings = {
|
||||
INFOPLIST_FILE = FeedFinderTests/Info.plist;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
84BAAE191C8E6B3B009F5239 /* Build configuration list for PBXProject "FeedFinder" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
84BAAE311C8E6B3B009F5239 /* Debug */,
|
||||
84BAAE321C8E6B3B009F5239 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
84BAAE331C8E6B3B009F5239 /* Build configuration list for PBXNativeTarget "FeedFinder" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
84BAAE341C8E6B3B009F5239 /* Debug */,
|
||||
84BAAE351C8E6B3B009F5239 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
84BAAE361C8E6B3B009F5239 /* Build configuration list for PBXNativeTarget "FeedFinderTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
84BAAE371C8E6B3B009F5239 /* Debug */,
|
||||
84BAAE381C8E6B3B009F5239 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 84BAAE161C8E6B3B009F5239 /* Project object */;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:RSFeedFinder.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
@ -1,29 +0,0 @@
|
||||
//
|
||||
// FeedFinder.h
|
||||
// FeedFinder
|
||||
//
|
||||
// Created by Brent Simmons on 3/7/16.
|
||||
// Copyright © 2016 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
//! Project version number for FeedFinder.
|
||||
FOUNDATION_EXPORT double FeedFinderVersionNumber;
|
||||
|
||||
//! Project version string for FeedFinder.
|
||||
FOUNDATION_EXPORT const unsigned char FeedFinderVersionString[];
|
||||
|
||||
// In this header, you should import all the public headers of your framework using statements like #import <FeedFinder/PublicHeader.h>
|
||||
|
||||
|
||||
/* Given a URL, find one or more feeds.
|
||||
|
||||
Download URL
|
||||
If is feed, return URL
|
||||
If is web page, find possible feeds in web page
|
||||
Download each possible URL
|
||||
If URL is feed, add to URLs list
|
||||
return URLs list
|
||||
*/
|
||||
|
@ -1,28 +0,0 @@
|
||||
<?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>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2016 Ranchero Software, LLC. All rights reserved.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
@ -1,39 +0,0 @@
|
||||
//
|
||||
// FeedFinderTests.m
|
||||
// FeedFinderTests
|
||||
//
|
||||
// Created by Brent Simmons on 3/7/16.
|
||||
// Copyright © 2016 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@interface FeedFinderTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation FeedFinderTests
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testExample {
|
||||
// This is an example of a functional test case.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
}
|
||||
|
||||
- (void)testPerformanceExample {
|
||||
// This is an example of a performance test case.
|
||||
[self measureBlock:^{
|
||||
// Put the code you want to measure the time of here.
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
@ -1,66 +0,0 @@
|
||||
//
|
||||
// HTMLFeedFinderTests.swift
|
||||
// FeedFinder
|
||||
//
|
||||
// Created by Brent Simmons on 8/7/16.
|
||||
// Copyright © 2016 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import FeedFinder
|
||||
import RSParser
|
||||
|
||||
class HTMLFeedFinderTests: XCTestCase {
|
||||
|
||||
func parserData(_ filename: String, _ fileExtension: String, _ url: String) -> ParserData {
|
||||
|
||||
let bundle = Bundle(for: HTMLFeedFinderTests.self)
|
||||
let path = bundle.path(forResource: filename, ofType: fileExtension)!
|
||||
let data = try! Data(contentsOf: URL(fileURLWithPath: path))
|
||||
return ParserData(url: url, data: data)
|
||||
}
|
||||
|
||||
func feedFinder(_ fileName: String, _ fileExtension: String, _ url: String) -> HTMLFeedFinder {
|
||||
|
||||
let d = parserData(fileName, fileExtension, url)
|
||||
return HTMLFeedFinder(parserData: d)
|
||||
}
|
||||
|
||||
func testPerformanceWithDaringFireball() {
|
||||
|
||||
let d = parserData("DaringFireball", "html", "https://daringfireball.net/")
|
||||
self.measure {
|
||||
|
||||
let finder = HTMLFeedFinder(parserData: d)
|
||||
let _ = finder.feedSpecifiers
|
||||
}
|
||||
}
|
||||
|
||||
func testFindingBestFeedWithDaringFireBall() {
|
||||
|
||||
let finder = feedFinder("DaringFireball", "html", "https://daringfireball.net/")
|
||||
let feedSpecifiers = finder.feedSpecifiers
|
||||
let bestFeedSpecifier = FeedSpecifier.bestFeed(in: feedSpecifiers)!
|
||||
XCTAssert(bestFeedSpecifier.urlString == "https://daringfireball.net/feeds/json")
|
||||
}
|
||||
|
||||
func testFindingBestFeedWithFurbo() {
|
||||
|
||||
let finder = feedFinder("furbo", "html", "http://furbo.org")
|
||||
let feedSpecifiers = finder.feedSpecifiers
|
||||
let bestFeedSpecifier = FeedSpecifier.bestFeed(in: feedSpecifiers)!
|
||||
XCTAssert(bestFeedSpecifier.urlString == "http://furbo.org/feed/")
|
||||
}
|
||||
|
||||
func testFindingBestFeedWithIndieStack() {
|
||||
|
||||
let finder = feedFinder("indiestack", "html", "http://indiestack.com/")
|
||||
let feedSpecifiers = finder.feedSpecifiers
|
||||
let bestFeedSpecifier = FeedSpecifier.bestFeed(in: feedSpecifiers)!
|
||||
XCTAssert(bestFeedSpecifier.urlString == "http://indiestack.com/feed/json/")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,24 +0,0 @@
|
||||
<?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>
|
File diff suppressed because it is too large
Load Diff
@ -1,372 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-US" class="no-js">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<!-- <meta name="viewport" content="width=device-width">-->
|
||||
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<meta http-equiv="Content-Language" content="en-us" />
|
||||
|
||||
<meta name="description" content="furbo.org is Craig Hockenberry's place to write for the web. He makes app and runs websites." />
|
||||
|
||||
<meta name="keywords" content="chock, woot" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="robots" content="NOODP">
|
||||
|
||||
<title>furbo.org by Craig Hockenberry</title>
|
||||
|
||||
<link rel="alternate" type="application/rss+xml" title="Iconfactory News Feed" href="http://furbo.org/feed/" />
|
||||
|
||||
<link href="http://furbo.org/wp-content/themes/furbo/reset.css?ver=20150205" media="screen" rel="Stylesheet" type="text/css" />
|
||||
<link href="http://furbo.org/wp-content/themes/furbo/style.css?ver=20150206" media="screen" rel="Stylesheet" type="text/css" />
|
||||
|
||||
<link rel="shortcut icon" href="http://furbo.org/favicon.ico" />
|
||||
|
||||
<script type="text/javascript" src="http://furbo.org/wp-content/themes/furbo/scripts/minified.js"></script>
|
||||
<script type="text/javascript" src="http://furbo.org/wp-content/themes/furbo/scripts/furbo.js"></script>
|
||||
</head>
|
||||
|
||||
<body class="home blog">
|
||||
<div id="page" class="hfeed site">
|
||||
<a class="skip-link screen-reader-text" href="#content">Skip to content</a>
|
||||
|
||||
<header id="masthead" class="site-header" role="banner">
|
||||
<h1 class="site-title screen-reader-text">
|
||||
<a href="http://furbo.org/" rel="home">
|
||||
furbo.org </a>
|
||||
</h1>
|
||||
|
||||
<div id="logo">
|
||||
<a href="http://furbo.org/" rel="home">
|
||||
furbo.org </a>
|
||||
</div>
|
||||
<div id="collapsed-logo">
|
||||
<a href="http://furbo.org/" rel="home">
|
||||
furbo.org </a>
|
||||
</div>
|
||||
<div id="controls">
|
||||
<p id="control-info" onclick="toggleSidebar();">info</p>
|
||||
</div>
|
||||
</header><!-- .site-header -->
|
||||
|
||||
<div id="background">
|
||||
</div>
|
||||
|
||||
<div id="sidebar" class="sidebar">
|
||||
<div id="secondary" class="secondary">
|
||||
|
||||
<aside id="about-me" class="widget widget_text">
|
||||
<h2 class="widget-title">About Me</h2>
|
||||
<div class="textwidget">
|
||||
<p>
|
||||
I'm Craig Hockenberry and this is where I write for the web. I make <a href="http://iconfactoryapps.com">apps</a> and run <a href="http://iconfactory.com">websites</a>.
|
||||
</p>
|
||||
<p>
|
||||
You can learn <a href="/about">more about me</a>, view <a href="/resume">my résumé</a>, or <a href="http://twitter.com/chockenberry">follow me</a>.
|
||||
</p>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div id="widget-area" class="widget-area" role="complementary">
|
||||
<aside id="search-3" class="widget widget_search"><form role="search" method="get" class="search-form" action="http://furbo.org/">
|
||||
<label>
|
||||
<span class="screen-reader-text">Search for:</span>
|
||||
<input type="search" class="search-field" placeholder="Search …" value="" name="s" title="Search for:" />
|
||||
</label>
|
||||
<input type="submit" class="search-submit" value="Search" />
|
||||
</form></aside> <aside id="recent-writing-3" class="widget widget_recent_entries"> <h2 class="widget-title">Recent Writing</h2> <ul>
|
||||
<li>
|
||||
<a href="http://furbo.org/2016/02/20/the-forensic-shit-show/">The Forensic Shit Show</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://furbo.org/2015/12/29/strapped-in/">Strapped In</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://furbo.org/2015/12/28/the-new-ipod/">The New iPod</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://furbo.org/2015/11/04/a-responsive-factory/">A Responsive Factory</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://furbo.org/2015/10/07/ipulse-3/">iPulse 3</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://furbo.org/2015/07/23/code-signing-in-el-capitan/">Code Signing in El Capitan</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://furbo.org/2015/07/22/half-assed/">Half-Assed</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://furbo.org/2015/07/14/a-watch-water-and-workouts/">A Watch, Water and Workouts</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://furbo.org/2015/07/09/i-left-my-system-fonts-in-san-francisco/">I Left My System Fonts in San Francisco</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://furbo.org/2015/06/15/bug-writing-season/">Bug Writing Season</a>
|
||||
</li>
|
||||
</ul>
|
||||
</aside><aside id="history-2" class="widget widget_archive"><h2 class="widget-title">Archives</h2> <ul>
|
||||
<li><a href='http://furbo.org/2016/'>2016</a></li>
|
||||
<li><a href='http://furbo.org/2015/'>2015</a></li>
|
||||
<li><a href='http://furbo.org/2014/'>2014</a></li>
|
||||
<li><a href='http://furbo.org/2013/'>2013</a></li>
|
||||
<li><a href='http://furbo.org/2012/'>2012</a></li>
|
||||
<li><a href='http://furbo.org/2011/'>2011</a></li>
|
||||
<li><a href='http://furbo.org/2010/'>2010</a></li>
|
||||
<li><a href='http://furbo.org/2009/'>2009</a></li>
|
||||
<li><a href='http://furbo.org/2008/'>2008</a></li>
|
||||
<li><a href='http://furbo.org/2007/'>2007</a></li>
|
||||
</ul>
|
||||
</aside> </div><!-- .widget-area -->
|
||||
|
||||
<aside id="other" class="widget widget_text">
|
||||
<div class="textwidget">
|
||||
<p>
|
||||
<a href="http://furbo.org/feed">RSS</a>
|
||||
</p>
|
||||
<p>
|
||||
© 2016 Craig Hockenberry
|
||||
</p>
|
||||
<p>
|
||||
All rights reserved
|
||||
</p>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
</div><!-- .secondary -->
|
||||
|
||||
</div><!-- .sidebar -->
|
||||
|
||||
<div id="content" class="site-content">
|
||||
|
||||
<div id="primary" class="content-area">
|
||||
<main id="main" class="site-main" role="main">
|
||||
|
||||
<!-- index is_home = 1 is_front_page = 1-->
|
||||
|
||||
|
||||
|
||||
|
||||
<article id="post-1984" class="post-1984 post type-post status-publish format-standard hentry category-miscellaneous category-opinion">
|
||||
|
||||
<div class="entry-header">
|
||||
<h2 class="entry-title"><a href="http://furbo.org/2016/02/20/the-forensic-shit-show/" rel="bookmark">The Forensic Shit Show</a></h2> </div><!-- .entry-header -->
|
||||
|
||||
<div class="entry-content">
|
||||
<p>It turns out someone at the FBI advised another law enforcement officer in San Bernardino to <a href="https://twitter.com/countywire/status/700887823482630144" onclick="javascript:pageTracker._trackPageview('/outbound/article/twitter.com');">reset the iPhone</a> that the government wants Apple to unlock.</p>
|
||||
<p>This is just another episode in a complete forensic shit show.</p>
|
||||
<p>Remember, this is the same case where <a href="http://www.nbcnews.com/storyline/san-bernardino-shooting/go-inside-home-syed-farook-tashfeen-malik-n474601" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.nbcnews.com');">the media was allowed to roam freely through a crime scene</a>. One of the photos in that gallery <a href="http://media4.s-nbcnews.com/j/msnbc/components/photo/_new/ss-151204-farook-apartment-jsw-13.nbcnews-ux-1024-900.jpg" onclick="javascript:pageTracker._trackPageview('/outbound/article/media4.s-nbcnews.com');">shows a computer without an Ethernet connection on the wall</a> (the age of the apartment also suggests that there would be no wired Internet.)</p>
|
||||
<p>What are the chances that there was a wireless network in that apartment? What are the chances that there are IP logs on that router? Or maybe some kind of data backed up to a disk on the router? Here’s another wild guess: maybe that router was used to <a href="https://twitter.com/kennwhite/status/700865678274752512" onclick="javascript:pageTracker._trackPageview('/outbound/article/twitter.com');">connect to an online backup service</a>.</p>
|
||||
<p>Yep, someone did the equivalent of a “restore factory defaults” on a device under active investigation.</p>
|
||||
<p>What we’re seeing here is law enforcement’s complete lack of understanding of how digital devices store and transmit data. This new evidence is much more intricate than smoking guns or blood splatters. The important stuff is what you don’t see: it’s a hard problem where the people dealing with it are untrained. Shit, I work in this business and trying to decipher what’s going on makes my head spin.</p>
|
||||
<p>Yet law enforcement is asking Apple to not only provide data, but also to create a forensic <strong>instrument</strong> that allows them to extract information from any device. And by its very nature, <a href="http://www.zdziarski.com/blog/?p=5645" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.zdziarski.com');">this tool would be made widely available throughout the forensic and law enforcement community</a>.</p>
|
||||
<p>Basically, the government is asking Apple to hand over a golden key that can defeat the security of <strong>any</strong> device to folks that can’t even secure a wireless network. Worse, this whole process is being overseen by politicians that think the problem is <a href="http://thehill.com/policy/cybersecurity/262658-feinstein-vows-to-offer-encryption-piercing-bill" onclick="javascript:pageTracker._trackPageview('/outbound/article/thehill.com');">predators getting access to their grandkid’s Playstation</a>.</p>
|
||||
<p>This is why the entire tech community is saying “No fucking way.”</p>
|
||||
<p><strong>Updated February 21st, 2016:</strong> Several people have commented about my use of “restore factory defaults” in the post above. My intention was figurative, not literal.</p>
|
||||
<p>The folks involved with the investigation were pressing buttons without understanding the consequences of their actions. To me, it feels like a “reboot to fix” approach. The password reset did not damage any data, it just made automatic backups stop working because iCloud information on the device needed to be updated, and that can’t be done without a passcode.</p>
|
||||
<p>Others have reminded me that the FBI had cleared the crime scene. That’s true, but since the Wi-Fi equipment was not collected as evidence, it still shows that the investigators were out of their league. In an electronic investigation, a router is a key piece of the puzzle.</p>
|
||||
<p>Both of these things are details in a bigger picture: the FBI wants to hold the private keys to a public key encryption system that affects the privacy of hundreds of millions people. If they can’t get the details of an online backup service right, how the hell do we expect them to guard a back door?</p>
|
||||
<p>There’s also <a href="http://daringfireball.net/2016/02/san_bernardino_password_reset" onclick="javascript:pageTracker._trackPageview('/outbound/article/daringfireball.net');">a possibility that the iCloud password reset was intentional</a>. If this is the case, we have a government that is extorting Apple by essentially planting evidence. Imagine what they could do with a private key.</p>
|
||||
</div><!-- .entry-content -->
|
||||
|
||||
<div class="entry-footer">
|
||||
Written on February 20th, 2016 for <a href="http://furbo.org/category/miscellaneous/" title="View all posts in Miscellaneous" rel="category tag">Miscellaneous</a>, <a href="http://furbo.org/category/opinion/" title="View all posts in Opinion" rel="category tag">Opinion</a> </div><!-- .entry-footer -->
|
||||
|
||||
</article><!-- #post-## -->
|
||||
|
||||
<article id="post-1953" class="post-1953 post type-post status-publish format-standard hentry category-miscellaneous category-observation">
|
||||
|
||||
<div class="entry-header">
|
||||
<h2 class="entry-title"><a href="http://furbo.org/2015/12/29/strapped-in/" rel="bookmark">Strapped In</a></h2> </div><!-- .entry-header -->
|
||||
|
||||
<div class="entry-content">
|
||||
<p>A lot has happened since I purchased <a href="http://www.apple.com/shop/buy-watch/apple-watch-sport/42mm-silver-aluminum-case-with-blue-sport-band?product=MLC52LL/A&step=detail" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.apple.com');">my Apple Watch</a> on April 10th, 2015. One unexpected aspect to owning this device is my fascination with watch bands:</p>
|
||||
<figure>
|
||||
<a href="http://furbo.org/wp-content/uploads/2015/12/AppleWatchBands.jpeg" ><img src="http://furbo.org/wp-content/uploads/2015/12/AppleWatchBands.jpeg" alt="Apple Watch Bands" width="1120" height="862" class="aligncenter size-full wp-image-1954" /></a></p>
|
||||
<figcaption>My current collection of watch bands. And no, <a href="http://furbo.org/2015/05/22/apple-watch-ergonomics/" >the watch isn’t upside down</a>.</figcaption>
|
||||
</figure>
|
||||
<p>From left to right, in order of date purchased:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="http://www.apple.com/shop/buy-watch/apple-watch-sport/42mm-silver-aluminum-case-with-blue-sport-band?product=MLC52LL/A&step=detail" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.apple.com');">Sport Model with Blue Band</a> ($400) – I picked the aluminum watch with a blue band because I knew it would be spending a lot of time in the water. To date, I’ve <a href="http://furbo.org/2015/07/14/a-watch-water-and-workouts/" >used it 110 times for over 35 hours of swimming</a>.
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://www.apple.com/shop/product/MJ5F2/42mm-milanese-loop" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.apple.com');">Milanese Loop</a> ($150) – I was intrigued by this band as soon as I saw it during the video at the product announcement. I love how the metal feels a lot like fabric. It also dresses up the utilitarian Sport model so it doesn’t look out of place when I’m someplace nice.
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://www.clockworksynergy.com/shop/tech-straps/straps-for-apple-watch/2-piece-heavy-nato-straps/black-grey-two-piece-heavy-nato-nylon-watch-strap-fits-apple-watch/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.clockworksynergy.com');">Black & Silver Nylon</a> ($30) – This NATO-style band from Clockwork Synergy popped up on my <a href="https://twitter.com/capttaco/status/625339467080040448" onclick="javascript:pageTracker._trackPageview('/outbound/article/twitter.com');">Twitter timeline</a> thanks to my pal <a href="https://twitter.com/capttaco" onclick="javascript:pageTracker._trackPageview('/outbound/article/twitter.com');">Rob Rhyne</a>. I love that it dresses up the watch <strong>and</strong> is waterproof.
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://www.apple.com/shop/product/MLDJ2/42mm-productred-band" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.apple.com');">Red Sport</a> ($50) – When Apple started selling additional colors for the sport bands, getting one in my favorite color was a no-brainer. I also like that a little of my purchase goes to a worthwhile charity.
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://www.amazon.com/gp/product/B00JP09C7I/ref=as_li_tl?ie=UTF8&camp=1789&creative=390957&creativeASIN=B00JP09C7I&linkCode=as2&tag=furboorg-20&linkId=4LVZVTJFJG5OY4UU" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.amazon.com');">Orange Silicone</a> ($20) – This band by MoKo was <a href="https://twitter.com/mrgan/status/643847960850247680" onclick="javascript:pageTracker._trackPageview('/outbound/article/twitter.com');">another recommendation</a> from Twitter by <a href="https://twitter.com/mrgan" onclick="javascript:pageTracker._trackPageview('/outbound/article/twitter.com');">Neven Mrgan</a>. To me, the most interesting thing about this band is that it shows why Apple went with <a href="https://en.wikipedia.org/wiki/Fluoroelastomer" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"> fluoroelastomer</a> for their bands: it’s stiffer and “breathes” better than silicone.
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://www.lucrin.com/straps-and-watches/watch-straps/apple-watch/apple-band-42mm-elegance.htm" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.lucrin.com');">Black Goat Leather</a> ($200) – The leather bands from Apple are nice, but I prefer the classic look of this one from Lucrin. The company also offers a huge range of colors: my wife loves the dark green one I gave at Christmas.
|
||||
</li>
|
||||
</ul>
|
||||
<p>In this survey of my growing collection, there’s an interesting datapoint: the value of these bands ($450) exceeds the cost of the watch itself ($400).</p>
|
||||
<p>If Apple decides to change the interchange mechanism in some future version of the watch, I will have very little desire to upgrade. As I continue to “work in” my leather band, I hope I’ll be using them for a long time.</p>
|
||||
</div><!-- .entry-content -->
|
||||
|
||||
<div class="entry-footer">
|
||||
Written on December 29th, 2015 for <a href="http://furbo.org/category/miscellaneous/" title="View all posts in Miscellaneous" rel="category tag">Miscellaneous</a>, <a href="http://furbo.org/category/observation/" title="View all posts in Observation" rel="category tag">Observation</a> </div><!-- .entry-footer -->
|
||||
|
||||
</article><!-- #post-## -->
|
||||
|
||||
<article id="post-1942" class="post-1942 post type-post status-publish format-standard hentry category-business category-observation">
|
||||
|
||||
<div class="entry-header">
|
||||
<h2 class="entry-title"><a href="http://furbo.org/2015/12/28/the-new-ipod/" rel="bookmark">The New iPod</a></h2> </div><!-- .entry-header -->
|
||||
|
||||
<div class="entry-content">
|
||||
<p>Something tells me that there were a lot of Apple Watches under the tree this year:</p>
|
||||
<p><a href="http://furbo.org/wp-content/uploads/2015/09/ClickerDownloads.jpeg" ><img src="http://furbo.org/wp-content/uploads/2015/09/ClickerDownloads.jpeg" alt="Clicker Downloads for December 2015" width="560" height="966" class="aligncenter size-full wp-image-1948" /></a></p>
|
||||
<p>That graph shows the last month of downloads for my free <a href="https://itunes.apple.com/us/app/clicker-count-anything/id1043951998?ls=1&mt=8&uo=4&at=10l4G7&ct=FURBO" onclick="javascript:pageTracker._trackPageview('/outbound/article/itunes.apple.com');">Clicker</a> app for watchOS. Since this app does nothing on an iPhone or iPad, the only reason to get it is if you have a new watch.</p>
|
||||
<p>Many of us, myself included, originally thought of the Apple Watch as a device in and of itself. But the more I use the computer on my wrist, the more it feels like a satellite to the computer that’s sitting in my pocket.</p>
|
||||
<p>Accessories have always made great gifts for folks who love their computers. Giving the watch as a gift is a perfect option for someone who’s always playing around with the apps on their iPhone. Just like the iPod was an ideal match for someone who loved playing music on their desktop computer.</p>
|
||||
</div><!-- .entry-content -->
|
||||
|
||||
<div class="entry-footer">
|
||||
Written on December 28th, 2015 for <a href="http://furbo.org/category/business/" title="View all posts in Business" rel="category tag">Business</a>, <a href="http://furbo.org/category/observation/" title="View all posts in Observation" rel="category tag">Observation</a> </div><!-- .entry-footer -->
|
||||
|
||||
</article><!-- #post-## -->
|
||||
|
||||
<article id="post-1932" class="post-1932 post type-post status-publish format-link hentry category-miscellaneous">
|
||||
<div class="entry-header">
|
||||
<h2 class="entry-title"><a href="http://furbo.org/2015/11/19/clicker-1-1/" title="Permalink" class="permalink">►</a> <a href="https://itunes.apple.com/us/app/clicker-count-anything/id1043951998?ls=1&mt=8&uo=4&at=10l4G7&ct=FURBO">Clicker 1.1</a></h2> </div>
|
||||
<!-- .entry-header -->
|
||||
|
||||
<div class="entry-content">
|
||||
<p>The <a href="https://itunes.apple.com/us/app/clicker-count-anything/id1043951998?ls=1&mt=8&uo=4&at=10l4G7&ct=FURBO" onclick="javascript:pageTracker._trackPageview('/outbound/article/itunes.apple.com');">Make John Gruber Happy Edition™</a> of Clicker is now available. <a href="http://daringfireball.net/linked/2015/10/14/clicker" onclick="javascript:pageTracker._trackPageview('/outbound/article/daringfireball.net');">Finally.</a></p>
|
||||
</div>
|
||||
<!-- .entry-content -->
|
||||
|
||||
|
||||
|
||||
</article><!-- #post-## -->
|
||||
|
||||
<article id="post-1920" class="post-1920 post type-post status-publish format-standard hentry category-design category-development">
|
||||
|
||||
<div class="entry-header">
|
||||
<h2 class="entry-title"><a href="http://furbo.org/2015/11/04/a-responsive-factory/" rel="bookmark">A Responsive Factory</a></h2> </div><!-- .entry-header -->
|
||||
|
||||
<div class="entry-content">
|
||||
<p>Back in May 2014, we introduced a new <a href="http://iconfactory.com" onclick="javascript:pageTracker._trackPageview('/outbound/article/iconfactory.com');">Iconfactory home page</a>. One of the main design goals for that site was to make the layout a <a href="https://en.wikipedia.org/wiki/Responsive_web_design" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');">responsive web design</a>: the same site looked great whether you were looking at it on a desktop PC or an iPhone. Reading <a href="http://abookapart.com/products/responsive-web-design" onclick="javascript:pageTracker._trackPageview('/outbound/article/abookapart.com');">Ethan Marcotte’s book</a> was a revelation.</p>
|
||||
<p>Of course, that site was just a beginning. <a href="http://design.iconfactory.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/design.iconfactory.com');">We</a> <a href="http://iconfactoryapps.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/iconfactoryapps.com');">run</a> <a href="http://blog.iconfactory.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/blog.iconfactory.com');">a</a> <a href="http://flareapp.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/flareapp.com');">lot</a> <a href="http://twitterrific.com/ios" onclick="javascript:pageTracker._trackPageview('/outbound/article/twitterrific.com');">of</a> <a href="http://xscopeapp.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/xscopeapp.com');">web</a> <a href="http://ipulseapp.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/ipulseapp.com');">sites</a> (<a href="http://appdevmanual.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/appdevmanual.com');">including</a> <a href="http://astronutapp.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/astronutapp.com');">some</a> <a href="http://chameleonproject.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/chameleonproject.org');">you’ve</a> <a href="http://dine-o-matic.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/dine-o-matic.com');">probably</a> <a href="http://frenzic.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/frenzic.com');">never</a> <a href="http://pickintimeapp.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/pickintimeapp.com');">heard</a> <a href="http://pixelpalooza.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/pixelpalooza.com');">of</a> <a href="http://takefiveapp.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/takefiveapp.com');">before</a>). Clearly we had to pick our responsive battles.</p>
|
||||
<p>We started with an <a href="http://blog.iconfactory.com/2015/01/welcome-to-the-new-iconfactory-blog/" onclick="javascript:pageTracker._trackPageview('/outbound/article/blog.iconfactory.com');">update to our blog</a> in January 2015. In October, we updated our <a href="http://iconfactoryapps.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/iconfactoryapps.com');">iOS and OS X app catalog</a>. And yesterday we launched a <a href="http://design.iconfactory.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/design.iconfactory.com');">responsive design portfolio</a>.</p>
|
||||
<p>A year and a half after our first responsive design, we’ve hit a milestone. All of the sites listed in the Iconfactory’s red navigation bar are responsive designs and will display correctly on any device. Woo hoo!</p>
|
||||
<p>Along the way, we cleaned up some of our branding elements and worked toward a more consistent experience across all the sites. Check out the post at the Iconfactory about <a href="http://blog.iconfactory.com/2015/11/the-new-favicon/" onclick="javascript:pageTracker._trackPageview('/outbound/article/blog.iconfactory.com');">the new SVG icons in Safari</a> to see what that’s all about.</p>
|
||||
<p>It’s clear we’re at a point in time where the vast assortment of screens is daunting. If you haven’t thought about how your site works on this wide variety of devices, <a href="http://alistapart.com/article/responsive-web-design" onclick="javascript:pageTracker._trackPageview('/outbound/article/alistapart.com');">now is a great time to start</a>.</p>
|
||||
</div><!-- .entry-content -->
|
||||
|
||||
<div class="entry-footer">
|
||||
Written on November 4th, 2015 for <a href="http://furbo.org/category/design/" title="View all posts in Design" rel="category tag">Design</a>, <a href="http://furbo.org/category/development/" title="View all posts in Development" rel="category tag">Development</a> </div><!-- .entry-footer -->
|
||||
|
||||
</article><!-- #post-## -->
|
||||
<div id="post-navigation">
|
||||
<div class="older"><a href="http://furbo.org/page/2/" >Older</a></div>
|
||||
</div>
|
||||
|
||||
</main><!-- .site-main -->
|
||||
</div><!-- .content-area -->
|
||||
|
||||
|
||||
</div><!-- .site-content -->
|
||||
|
||||
<footer id="footer" class="site-footer" role="contentinfo">
|
||||
<div class="features">
|
||||
<div class="feature">
|
||||
<script type="text/javascript">
|
||||
var content = [];
|
||||
|
||||
content.push('\
|
||||
<a href="http://design.iconfactory.com">\
|
||||
<img src="/footer/designsite.png" width="198" height="101" />\
|
||||
</a>\
|
||||
<h5>Do you write awesome code?</h5>\
|
||||
<p>Of course, you do!</p>\
|
||||
<p>But if you\'re like me, you struggle with design and opening Photoshop makes you break out in a cold sweat.</p>\
|
||||
<p>\
|
||||
<a href="http://design.iconfactory.com">\
|
||||
The designers that help me can also help you…</a>\
|
||||
</p>\
|
||||
');
|
||||
|
||||
content.push('\
|
||||
<a href="http://xscopeapp.com">\
|
||||
<img src="/footer/xscope4.png" width="198" height="101" />\
|
||||
</a>\
|
||||
<h5>I made this app because I needed it.</h5>\
|
||||
<p>\
|
||||
If you do any kind of Web, iOS or Mac development, you need these \
|
||||
<a href="http://xscopeapp.com/guide">\
|
||||
awesome tools</a>. I did.\
|
||||
</p>\
|
||||
<p>\
|
||||
After a few days with xScope\'s \
|
||||
<a href="http://xscopeapp.com">\
|
||||
FREE trial</a>, you\'ll wonder how you ever did work without it. Trust me.\
|
||||
</p>\
|
||||
');
|
||||
|
||||
content.push('\
|
||||
<a href="http://furbo.org/2014/09/03/the-terminal/">\
|
||||
<img src="/footer/terminal.png" width="200" height="100" />\
|
||||
</a>\
|
||||
<h5>$ sudo rm -rf /</h5>\
|
||||
<p>You use this app every day, but do you know what happens when you option click in it?</p>\
|
||||
<p>\
|
||||
Find out in my magnum opus on \
|
||||
<a href="http://furbo.org/2014/09/03/the-terminal/">\
|
||||
The Terminal</a>.\
|
||||
</p>\
|
||||
');
|
||||
|
||||
content.push('\
|
||||
<a href="https://itunes.apple.com/us/app/ipulse/id1028916583?ls=1&mt=12&uo=4&at=10l4G7&ct=FURBO_FOOTER">\
|
||||
<img src="/footer/ipulse3.png" width="200" height="100" />\
|
||||
</a>\
|
||||
<h5>What is your Mac doing right now?</h5>\
|
||||
<p>If you\'re reading this site, chances are good that you know <em>a lot</em> about your computer.</p>\
|
||||
<p>\
|
||||
But do you know what\'s going on behind the scenes? \
|
||||
<a href="https://itunes.apple.com/us/app/ipulse/id1028916583?ls=1&mt=12&uo=4&at=10l4G7&ct=FURBO_FOOTER">\
|
||||
iPulse does</a>.\
|
||||
</p>\
|
||||
');
|
||||
|
||||
var min = 0;
|
||||
var max = content.length - 1;
|
||||
var index = Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
|
||||
document.write(content[index]);
|
||||
</script>
|
||||
|
||||
</div><!-- .feature -->
|
||||
</div><!-- .features -->
|
||||
</footer><!-- .site-footer -->
|
||||
|
||||
</div><!-- .site -->
|
||||
|
||||
|
||||
<!-- Google Analytics for WordPress | http://yoast.com/wordpress/google-analytics/ -->
|
||||
<script type="text/javascript">
|
||||
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
|
||||
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
try {
|
||||
var pageTracker = _gat._getTracker("UA-12748460-3");
|
||||
// Cookied already:
|
||||
pageTracker._trackPageview();
|
||||
} catch(err) {}
|
||||
</script><!-- End of Google Analytics code -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<!-- Dynamic Page Served (once) in 0.171 seconds -->
|
||||
<!-- super cache -->
|
@ -1,715 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!--[if IE 7]>
|
||||
<html class="ie ie7" lang="en-US">
|
||||
<![endif]-->
|
||||
<!--[if IE 8]>
|
||||
<html class="ie ie8" lang="en-US">
|
||||
<![endif]-->
|
||||
<!--[if !(IE 7) & !(IE 8)]><!-->
|
||||
<html lang="en-US">
|
||||
<!--<![endif]-->
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>Indie Stack | Hacking the Mac, iOS, and more with Daniel Jalkut</title>
|
||||
<link rel="profile" href="http://gmpg.org/xfn/11">
|
||||
<link rel="pingback" href="http://indiestack.com/xmlrpc.php">
|
||||
<!--[if lt IE 9]>
|
||||
<script src="http://indiestack.com/wp-content/themes/twentythirteen/js/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link rel='dns-prefetch' href='//fonts.googleapis.com' />
|
||||
<link rel='dns-prefetch' href='//s.w.org' />
|
||||
<link href='https://fonts.gstatic.com' crossorigin rel='preconnect' />
|
||||
<link rel="alternate" type="application/rss+xml" title="Indie Stack » Feed" href="http://indiestack.com/feed/" />
|
||||
<script type="text/javascript">
|
||||
window._wpemojiSettings = {"baseUrl":"https:\/\/s.w.org\/images\/core\/emoji\/2.3\/72x72\/","ext":".png","svgUrl":"https:\/\/s.w.org\/images\/core\/emoji\/2.3\/svg\/","svgExt":".svg","source":{"concatemoji":"http:\/\/indiestack.com\/wp-includes\/js\/wp-emoji-release.min.js?ver=4.8.3"}};
|
||||
!function(a,b,c){function d(a){var b,c,d,e,f=String.fromCharCode;if(!k||!k.fillText)return!1;switch(k.clearRect(0,0,j.width,j.height),k.textBaseline="top",k.font="600 32px Arial",a){case"flag":return k.fillText(f(55356,56826,55356,56819),0,0),b=j.toDataURL(),k.clearRect(0,0,j.width,j.height),k.fillText(f(55356,56826,8203,55356,56819),0,0),c=j.toDataURL(),b!==c&&(k.clearRect(0,0,j.width,j.height),k.fillText(f(55356,57332,56128,56423,56128,56418,56128,56421,56128,56430,56128,56423,56128,56447),0,0),b=j.toDataURL(),k.clearRect(0,0,j.width,j.height),k.fillText(f(55356,57332,8203,56128,56423,8203,56128,56418,8203,56128,56421,8203,56128,56430,8203,56128,56423,8203,56128,56447),0,0),c=j.toDataURL(),b!==c);case"emoji4":return k.fillText(f(55358,56794,8205,9794,65039),0,0),d=j.toDataURL(),k.clearRect(0,0,j.width,j.height),k.fillText(f(55358,56794,8203,9794,65039),0,0),e=j.toDataURL(),d!==e}return!1}function e(a){var c=b.createElement("script");c.src=a,c.defer=c.type="text/javascript",b.getElementsByTagName("head")[0].appendChild(c)}var f,g,h,i,j=b.createElement("canvas"),k=j.getContext&&j.getContext("2d");for(i=Array("flag","emoji4"),c.supports={everything:!0,everythingExceptFlag:!0},h=0;h<i.length;h++)c.supports[i[h]]=d(i[h]),c.supports.everything=c.supports.everything&&c.supports[i[h]],"flag"!==i[h]&&(c.supports.everythingExceptFlag=c.supports.everythingExceptFlag&&c.supports[i[h]]);c.supports.everythingExceptFlag=c.supports.everythingExceptFlag&&!c.supports.flag,c.DOMReady=!1,c.readyCallback=function(){c.DOMReady=!0},c.supports.everything||(g=function(){c.readyCallback()},b.addEventListener?(b.addEventListener("DOMContentLoaded",g,!1),a.addEventListener("load",g,!1)):(a.attachEvent("onload",g),b.attachEvent("onreadystatechange",function(){"complete"===b.readyState&&c.readyCallback()})),f=c.source||{},f.concatemoji?e(f.concatemoji):f.wpemoji&&f.twemoji&&(e(f.twemoji),e(f.wpemoji)))}(window,document,window._wpemojiSettings);
|
||||
</script>
|
||||
<style type="text/css">
|
||||
img.wp-smiley,
|
||||
img.emoji {
|
||||
display: inline !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
height: 1em !important;
|
||||
width: 1em !important;
|
||||
margin: 0 .07em !important;
|
||||
vertical-align: -0.1em !important;
|
||||
background: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
</style>
|
||||
<link rel='stylesheet' id='parent-style-css' href='http://indiestack.com/wp-content/themes/twentythirteen/style.css?ver=4.8.3' type='text/css' media='all' />
|
||||
<link rel='stylesheet' id='twentythirteen-fonts-css' href='https://fonts.googleapis.com/css?family=Source+Sans+Pro%3A300%2C400%2C700%2C300italic%2C400italic%2C700italic%7CBitter%3A400%2C700&subset=latin%2Clatin-ext' type='text/css' media='all' />
|
||||
<link rel='stylesheet' id='genericons-css' href='http://indiestack.com/wp-content/themes/twentythirteen/genericons/genericons.css?ver=3.03' type='text/css' media='all' />
|
||||
<link rel='stylesheet' id='twentythirteen-style-css' href='http://indiestack.com/wp-content/themes/indiestack-new/style.css?ver=2013-07-18' type='text/css' media='all' />
|
||||
<!--[if lt IE 9]>
|
||||
<link rel='stylesheet' id='twentythirteen-ie-css' href='http://indiestack.com/wp-content/themes/twentythirteen/css/ie.css?ver=2013-07-18' type='text/css' media='all' />
|
||||
<![endif]-->
|
||||
<script type='text/javascript' src='http://indiestack.com/wp-includes/js/jquery/jquery.js?ver=1.12.4'></script>
|
||||
<script type='text/javascript' src='http://indiestack.com/wp-includes/js/jquery/jquery-migrate.min.js?ver=1.4.1'></script>
|
||||
<link rel='https://api.w.org/' href='http://indiestack.com/wp-json/' />
|
||||
<link rel="EditURI" type="application/rsd+xml" title="RSD" href="http://indiestack.com/xmlrpc.php?rsd" />
|
||||
<link rel="wlwmanifest" type="application/wlwmanifest+xml" href="http://indiestack.com/wp-includes/wlwmanifest.xml" />
|
||||
<meta name="generator" content="WordPress 4.8.3" />
|
||||
<link rel="alternate" type="application/json" title="JSON Feed" href="http://indiestack.com/feed/json/" /> <style type="text/css">.recentcomments a{display:inline !important;padding:0 !important;margin:0 !important;}</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
//QuickLogin by Red Sweater Software
|
||||
|
||||
document['onkeyup'] = function(event){
|
||||
var e = event || window.event;
|
||||
|
||||
var triggerKeyCode = 27;
|
||||
var loginPageURL = "http://indiestack.com/wp-login.php?redirect_to=http%3A%2F%2Findiestack.com%2F";
|
||||
|
||||
if ( e.keyCode == triggerKeyCode ) {
|
||||
document.location.href=loginPageURL;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style type="text/css" id="twentythirteen-header-css">
|
||||
.site-header {
|
||||
background: url(http://indiestack.com/wp-content/themes/twentythirteen/images/headers/star.png) no-repeat scroll top;
|
||||
background-size: 1600px auto;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.site-header {
|
||||
background-size: 768px auto;
|
||||
}
|
||||
}
|
||||
@media (max-width: 359px) {
|
||||
.site-header {
|
||||
background-size: 360px auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="home blog single-author">
|
||||
<div id="page" class="hfeed site">
|
||||
<header id="masthead" class="site-header" role="banner">
|
||||
<a class="home-link" href="http://indiestack.com/" title="Indie Stack" rel="home">
|
||||
<h1 class="site-title">Indie Stack</h1>
|
||||
<h2 class="site-description">Hacking the Mac, iOS, and more with Daniel Jalkut</h2>
|
||||
</a>
|
||||
|
||||
<div id="navbar" class="navbar">
|
||||
<nav id="site-navigation" class="navigation main-navigation" role="navigation">
|
||||
<button class="menu-toggle">Menu</button>
|
||||
<a class="screen-reader-text skip-link" href="#content" title="Skip to content">Skip to content</a>
|
||||
<div class="menu-daniels-other-stuff-container"><ul id="primary-menu" class="nav-menu"><li id="menu-item-124" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-124"><a href="http://www.red-sweater.com/">Red Sweater Software</a></li>
|
||||
<li id="menu-item-125" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-125"><a href="http://coreint.org/">Core Intuition Podcast</a></li>
|
||||
<li id="menu-item-126" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-126"><a href="http://bitsplitting.org/">Bitsplitting Blog</a></li>
|
||||
<li id="menu-item-127" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-127"><a href="http://punkitup.com/twitpop/">TwitPOP</a></li>
|
||||
</ul></div> <form role="search" method="get" class="search-form" action="http://indiestack.com/">
|
||||
<label>
|
||||
<span class="screen-reader-text">Search for:</span>
|
||||
<input type="search" class="search-field" placeholder="Search …" value="" name="s" />
|
||||
</label>
|
||||
<input type="submit" class="search-submit" value="Search" />
|
||||
</form> </nav><!-- #site-navigation -->
|
||||
</div><!-- #navbar -->
|
||||
</header><!-- #masthead -->
|
||||
|
||||
<div id="main" class="site-main">
|
||||
|
||||
<div id="primary" class="content-area">
|
||||
<div id="content" class="site-content" role="main">
|
||||
|
||||
|
||||
<article id="post-581" class="post-581 post type-post status-publish format-standard hentry category-bug-reports category-xcode">
|
||||
<header class="entry-header">
|
||||
|
||||
<h1 class="entry-title">
|
||||
<a href="http://indiestack.com/2017/11/treat-warnings-as-errors-in-swift/" rel="bookmark">Treat Warnings as Errors in Swift</a>
|
||||
</h1>
|
||||
|
||||
<div class="entry-meta">
|
||||
<span class="date"><a href="http://indiestack.com/2017/11/treat-warnings-as-errors-in-swift/" title="Permalink to Treat Warnings as Errors in Swift" rel="bookmark"><time class="entry-date" datetime="2017-11-04T17:57:50+00:00">November 4, 2017</time></a></span><span class="categories-links"><a href="http://indiestack.com/category/bug-reports/" rel="category tag">Bug Reports</a>, <a href="http://indiestack.com/category/xcode/" rel="category tag">Xcode</a></span><span class="author vcard"><a class="url fn n" href="http://indiestack.com/author/admin/" title="View all posts by Daniel Jalkut" rel="author">Daniel Jalkut</a></span> </div><!-- .entry-meta -->
|
||||
</header><!-- .entry-header -->
|
||||
|
||||
<div class="entry-content">
|
||||
<p>For years I have maintained a zero-tolerance policy for warnings in shipping code. To help me enforce this, my “Release” build configurations define the build setting that induces Xcode to “treat warnings as errors”:</p>
|
||||
<pre>GCC_TREAT_WARNINGS_AS_ERRORS = YES
|
||||
</pre>
|
||||
<p>The comedy of this build setting is that it references “GCC,” a compiler that increasingly few of us even remember Apple ever using for macOS or iOS development. Apple’s fork of the popular open-source compiler was the standard for years, until Apple debuted clang, which is built on the LLVM framework.</p>
|
||||
<p>It’s always been kind of annoying that, as time moved on, we were stuck specifying important build settings with names that included “GCC,” but I accepted it. After all, I configure the vast majority of these settings in “.xcconfig” files that I reuse in all my projects, so I almost never interact with them directly.</p>
|
||||
<p>As I’ve started to shift some of my code from Objective-C to Swift, I assumed that my ancient GCC build setting would ensure that I don’t ship any builds with warnings. But today I realized that I had built a release build that <em>didn’t treat a warning as an error</em>. What’s the deal?</p>
|
||||
<p>It looks like Apple has decided to break with tradition and establish a new build setting for Swift:</p>
|
||||
<pre>SWIFT_TREAT_WARNINGS_AS_ERRORS = YES
|
||||
</pre>
|
||||
<p>Adding this to my .xcconfig file breaks the build when a warning is generated, and prevents me from accidentally shipping code that is known to be vulnerable to unexpected behavior. I think it would have been a nice touch if Apple had inferred SWIFT_TREAT_WARNINGS_AS_ERRORS when GCC_TREAT_WARNINGS_AS_ERRORS is set, so I’ve filed a bug requesting that it does. Radar #35352318.</p>
|
||||
</div><!-- .entry-content -->
|
||||
|
||||
<footer class="entry-meta">
|
||||
|
||||
</footer><!-- .entry-meta -->
|
||||
</article><!-- #post -->
|
||||
|
||||
<article id="post-574" class="post-574 post type-post status-publish format-standard hentry category-cocoa category-os-x category-swift">
|
||||
<header class="entry-header">
|
||||
|
||||
<h1 class="entry-title">
|
||||
<a href="http://indiestack.com/2017/10/selective-selector-mapping/" rel="bookmark">Selective Selector Mapping</a>
|
||||
</h1>
|
||||
|
||||
<div class="entry-meta">
|
||||
<span class="date"><a href="http://indiestack.com/2017/10/selective-selector-mapping/" title="Permalink to Selective Selector Mapping" rel="bookmark"><time class="entry-date" datetime="2017-10-31T10:41:41+00:00">October 31, 2017</time></a></span><span class="categories-links"><a href="http://indiestack.com/category/cocoa/" rel="category tag">Cocoa</a>, <a href="http://indiestack.com/category/os-x/" rel="category tag">OS X</a>, <a href="http://indiestack.com/category/swift/" rel="category tag">Swift</a></span><span class="author vcard"><a class="url fn n" href="http://indiestack.com/author/admin/" title="View all posts by Daniel Jalkut" rel="author">Daniel Jalkut</a></span> </div><!-- .entry-meta -->
|
||||
</header><!-- .entry-header -->
|
||||
|
||||
<div class="entry-content">
|
||||
<p>I ran into an interesting challenge while porting some Objective-C code to Swift. The class in question served both as an NSTableView delegate and data source, meaning that it implemented methods both for controlling the table view’s behavior and for supplying its content.</p>
|
||||
<p>Historically in Cocoa, most delegate relationships were established as <em>informal</em> protocols. If you wanted a particular class to <em>be a table view data source</em>, you simply implemented the required methods. For example, to populate a cell based table view, a data source would implement various methods, including one to indicate how many rows the view should have:</p>
|
||||
<pre>
|
||||
- (NSInteger) numberOfRowsInTableView:(NSTableView *)tableView;
|
||||
</pre>
|
||||
<p>In recent years, Apple has increasingly converted these informal protocols to <em>formal</em> Objective-C protocols. These give the compiler the opportunity to generate errors if a particular class declares compliance, but neglects to implement a required method. At runtime, however, the compliance-checking is still pretty loose. NSTableView consults its data source, checks to see that it implements a required subset of methods, and dynamically dispatches to them if it does.</p>
|
||||
<p>The dynamic nature of NSTableView hasn’t changed with Swift. An @objc class in Swift that complies with NSTableViewDataSource must still implement the required methods such that Apple’s Objective-C based NSTableView can dynamically look up and dispatch to the required delegate methods. Swift’s method rewriting “magic” even ensures that a delegate method can be written in modern Swift style, yet still appear identically to older Objective-C code:</p>
|
||||
<pre>
|
||||
class MyDataSource: NSObject {
|
||||
@objc func numberOfRows(in tableView: NSTableView) -> Int {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
<p>Given an instance of MyDataSource, I can use the Objective-C runtime to confirm that a the legacy “numberOfRowsInTableView:” selector is actually implemented by the class above:</p>
|
||||
<pre>
|
||||
let thisSource = MyDataSource()
|
||||
thisSource.responds(to: Selector("numberOfRowsInTableView:")) // false
|
||||
</pre>
|
||||
<p>Or can I? False? That’s no good. I’m using the discouraged “Selector” initializer here to ensure I get Swift to look for a very specific Selector, even if it doesn’t appear to be correct to the Swift-adapted side of the runtime.</p>
|
||||
<p>I was scratching my head, trying to figure out why Objective-C could not <em>see</em> my method. Did I forget an @objc marker? No. Did I forget to make MyDataSource a subclass of NSObject? No. I finally discovered that I could second-guess the default Swift selector mapping to obtain a result that “worked”:</p>
|
||||
<pre>
|
||||
class MyDataSource: NSObject {
|
||||
@objc func numberOfRowsInTableView(_ tableView: NSTableView) -> Int {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
let thisSource = MyDataSource()
|
||||
thisSource.responds(to: Selector("numberOfRowsInTableView:")) // true
|
||||
</pre>
|
||||
<p>Instances of MyDataSource will get the job done for Objective-C calls to “numberOfRowsInTableView:”, but I’ve lost all the pretty formatting that I expected to be able to use in Swift.</p>
|
||||
<p>There’s something else I’m missing out in my Swift implementation: type checking of MyDataSource’s compliance with the NSTableViewDataSource protocol. Old habits die hard, and I had initially ported my class over with an old-fashioned, informal approach to complying with NSTableViewDataSource: I declared a plain NSObject that <em>happens to implement</em> the informal protocol.</p>
|
||||
<p>It turns that adding that protocol conformance onto my class declaration not only gains me Swift’s protocol type checking, but <em>changes</em> the way key functions are mapped from Swift to Objective-C:</p>
|
||||
<pre>
|
||||
class MyDataSource: NSObject, NSTableViewDataSource {
|
||||
func numberOfRows(in tableView: NSTableView) -> Int {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
let thisSource = MyDataSource()
|
||||
thisSource.responds(to: Selector("numberOfRowsInTableView:")) // true
|
||||
</pre>
|
||||
<p>Armed with the knowledge that my class <em>intends to comply</em> with NSTableViewDataSource, Swift generates the expected mapping to Objective-C. Notice in this final case, I don’t even have to remember to mark the function as @objc. I guess when Swift is creating the selector mapping for a function, it does so in a few phases, prioritizing more explicit scenarios over more general:</p>
|
||||
<ol>
|
||||
<li>First, it defers to any explicit annotation with the @objc attribute. If I tag my “numberOfRows…” func above with “@objc(numberOfDoodads:)” then the method will be made available to Objective-C code dynamically looking for “numberOfDoodads:”.</li>
|
||||
<li>If there’s no @objc specialization, it tries to match function implementations with declarations in superclasses or protocols the class complies with. This is what gives us the automatic mapping of Swift’s “numberOfRows(in:)” to Objective-C’s “numberOfRowsInTableView:”.</li>
|
||||
<li>Finally it resorts to a default mapping based on <a href="https://swift.org/documentation/api-design-guidelines/">Swift API Design Guidelines</a>. This is what yielded the default “numberOfRowsIn:” mapping that I first encountered.</li>
|
||||
</ol>
|
||||
<p>This is an example of a Swift growing pain that is particularly likely to affect folks who are adapting older source bases (and older programming mindsets!) to Swift. If you run across a completely vexing failure of Objective-C to acknowledge your Swift class’s protocol compliance, start by making sure that you’ve actually declared the compliance in your class declaration!</p>
|
||||
</div><!-- .entry-content -->
|
||||
|
||||
<footer class="entry-meta">
|
||||
|
||||
</footer><!-- .entry-meta -->
|
||||
</article><!-- #post -->
|
||||
|
||||
<article id="post-570" class="post-570 post type-post status-publish format-standard hentry category-bug-reports category-cocoa category-mac category-os-x">
|
||||
<header class="entry-header">
|
||||
|
||||
<h1 class="entry-title">
|
||||
<a href="http://indiestack.com/2017/09/swatch-your-step/" rel="bookmark">Swatch Your Step</a>
|
||||
</h1>
|
||||
|
||||
<div class="entry-meta">
|
||||
<span class="date"><a href="http://indiestack.com/2017/09/swatch-your-step/" title="Permalink to Swatch Your Step" rel="bookmark"><time class="entry-date" datetime="2017-09-30T20:46:59+00:00">September 30, 2017</time></a></span><span class="categories-links"><a href="http://indiestack.com/category/bug-reports/" rel="category tag">Bug Reports</a>, <a href="http://indiestack.com/category/cocoa/" rel="category tag">Cocoa</a>, <a href="http://indiestack.com/category/mac/" rel="category tag">Mac</a>, <a href="http://indiestack.com/category/os-x/" rel="category tag">OS X</a></span><span class="author vcard"><a class="url fn n" href="http://indiestack.com/author/admin/" title="View all posts by Daniel Jalkut" rel="author">Daniel Jalkut</a></span> </div><!-- .entry-meta -->
|
||||
</header><!-- .entry-header -->
|
||||
|
||||
<div class="entry-content">
|
||||
<p>Shortly after macOS 10.13 was released, I received an oddly specific bug report from a customer, who observed that the little square “swatches” in the standard Mac color panel no longer had any effect on <a href="http://red-sweater.com/marsedit">MarsEdit’s</a> rich text editor.</p>
|
||||
<p><img title="Colors.png" src="http://indiestack.com/wp-content/uploads/2017/09/Colors.png" alt="Screenshot of the macOS standard color panel." width="236" height="341" border="0" /></p>
|
||||
<p>I was able to reproduce the problem in the shipping 3.7.11 version of MarsEdit, which for various reasons is still built using an older version of Xcode, against the 10.6 SDK. The <a href="https://red-sweater.com/blog/3244/marsedit-4-public-beta">MarsEdit 4 Beta</a>, which is built against the 10.12 SDK, <em>does not exhibit the problem.</em></p>
|
||||
<p>It’s not unusual for the behavior of Apple’s frameworks to vary based on the version of SDK an application was built against. The idea is usually to preserve the old behaviors of frameworks, so that any changes do not defy the expectations of a developer who has not been able to build and test their app against a later SDK. Sometimes, the variations in behavior lead to bugs like this one.</p>
|
||||
<p>Using a totally straightforward demo app, consisting only of an NSTextView and a button to bring up the color panel, I was able to confirm that the bug affects an app that links against the macOS 10.9 SDK, but does not affect an app that links against the 10.10 SDK.</p>
|
||||
<p>I filed Radar #34757710: “NSColorPanel swatches don’t work on apps linked against 10.9 or earlier.” I don’t know of a workaround yet, other than compiling against a later SDK.</p>
|
||||
</div><!-- .entry-content -->
|
||||
|
||||
<footer class="entry-meta">
|
||||
|
||||
</footer><!-- .entry-meta -->
|
||||
</article><!-- #post -->
|
||||
|
||||
<article id="post-560" class="post-560 post type-post status-publish format-standard hentry category-apple category-xcode">
|
||||
<header class="entry-header">
|
||||
|
||||
<h1 class="entry-title">
|
||||
<a href="http://indiestack.com/2017/09/xcode-9-signing-workarounds/" rel="bookmark">Xcode 9 Signing Workarounds</a>
|
||||
</h1>
|
||||
|
||||
<div class="entry-meta">
|
||||
<span class="date"><a href="http://indiestack.com/2017/09/xcode-9-signing-workarounds/" title="Permalink to Xcode 9 Signing Workarounds" rel="bookmark"><time class="entry-date" datetime="2017-09-27T16:08:14+00:00">September 27, 2017</time></a></span><span class="categories-links"><a href="http://indiestack.com/category/apple/" rel="category tag">Apple</a>, <a href="http://indiestack.com/category/xcode/" rel="category tag">Xcode</a></span><span class="author vcard"><a class="url fn n" href="http://indiestack.com/author/admin/" title="View all posts by Daniel Jalkut" rel="author">Daniel Jalkut</a></span> </div><!-- .entry-meta -->
|
||||
</header><!-- .entry-header -->
|
||||
|
||||
<div class="entry-content">
|
||||
<p>I wrote on Monday about <a href="http://indiestack.com/2017/09/sandbox-inheritance-tax/">issues with Xcode 9</a> relating to code signing. Although the gist of that post involved sandboxed Mac applications that launch sandboxed child processes, the fundamental issue is a bit broader: Xcode 9 adds a “com.apple.security.get-task-allow” entitlement to any binary it signs. For the majority of developers, this is probably not an issue, because the entitlement <em>is removed</em> when an Xcode archive is exported for distribution. <em>Most</em> developers, and particularly iOS developers, use Xcode archives.</p>
|
||||
<p>For folks who don’t, side effects of this additional entitlement include, but may not be limited to:</p>
|
||||
<ol>
|
||||
<li>Inability to launch sandboxed child processes.</li>
|
||||
<li>Rejection from the Mac App Store.</li>
|
||||
<li>Unknown consequences of shipping with an unintended entitlement.</li>
|
||||
</ol>
|
||||
<p>So, if you’re a developer who doesn’t use archives, what are your options? I’ve come up with four workarounds, and I present them here, roughly sorted by advisability and level of tedium:</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p><strong>Use Xcode 8.</strong> The simplest solution is to not upgrade to Xcode 9 unless and until you need to. Xcode 8’s signing process <em>does not impose</em> the unintended entitlement, so there is no risk of shipping a product that has it, unless you add it yourself. The downside to sticking with Xcode 8 is you won’t enjoy any of the new features of Xcode 9, you’ll have to work to support either Swift 4, macOS 10.13, or iOS 11 SDK features in your app.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Manually re-sign the built-product.</strong> Code signing is code signing, and you’re free to sign anything you like to suit your needs, using the “codesign” command line tool. It frankly sounds like a pain in the neck to recursively re-sign every binary in the app bundle, ensuring that the suitable entitlements (minus the unwanted one) are preserved, but I’m sure it can be done.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Use Xcode archives.</strong> It strikes me as a little obnoxious to have to use Xcode archives when they don’t offer any added benefits for my dibstrution workflow. But as a long term solution, this is probably the safest bet. The new behavior in Xcode 9 strongly suggests that Apple expects most developers to use archives, and joining the crowd is usually a good idea when it comes to avoiding trouble with Apple’s developer tools.</p>
|
||||
<p>If you are using Xcode archives for the first time, particularly with a complex project, you might discover that the resulting archives are not suitable for exporting a signed application. If you get a “Generic Xcode Archive” after running Build -> Archive, you know you’ve got a problem. By default the archive process builds all targets with an “install” option, rendering their built products into a file hierarchy that will be used to build the archive. If your project includes helper apps, for example, they will be “installed” alongside your main app, resulting in a generic archive of two apps, instead of the expected archive of a single app.</p>
|
||||
<p>The solution for this problem is to ensure that the “SKIP_INSTALL” build setting is set to YES for any such helper app. Just archive your main app, export the “Built Products” from the resulting archive, and look at the file hierarchy to determine whether you have subtargets that need to have installation disabled.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Hack Xcode 9.</strong> In a hurry to ship an update to your app, and you’ve only got Xcode 9 handy? It turns out the imposition of this “com.apple.security.get-task-allow” entitlement is controlled by a single property list file inside Xcode’s application bundle. As a test, I edited the file:</p>
|
||||
<pre>
|
||||
Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Xcode/PrivatePlugIns/IDEOSXSupportCore.ideplugin/Contents/Resources/BaseEntitlements.plist
|
||||
</pre>
|
||||
<p>It contains a single entitlement, the one that’s causing our grief. I deleted the entitlement from the list, saved the file, and relaunched Xcode. After doing so, everything is “back to normal.”</p>
|
||||
<p>I can’t strongly encourage you to hack your copy of Xcode because I don’t know what the consequences might be. “It seems fine,” but you’re on your own if you decide to do this.
|
||||
</li>
|
||||
</ol>
|
||||
<p>This small change in Xcode 9 causes a lot of unexpected grief for folks who don’t use Xcode archives. I am curious to know how widespread the problem is, and enthusiastic to get the word out about it so that affected folks can work around the problem, or at least be aware of it. Myself, I’ll probably end up adopting the workaround of using Xcode archives, but I’m hopeful that Apple will see the merit of providing an option in an update to Xcode 9 that supports disabling the addition of this entitlement without archiving and exporting a built product.</p>
|
||||
</div><!-- .entry-content -->
|
||||
|
||||
<footer class="entry-meta">
|
||||
|
||||
</footer><!-- .entry-meta -->
|
||||
</article><!-- #post -->
|
||||
|
||||
<article id="post-556" class="post-556 post type-post status-publish format-standard hentry category-cocoa category-os-x">
|
||||
<header class="entry-header">
|
||||
|
||||
<h1 class="entry-title">
|
||||
<a href="http://indiestack.com/2017/09/unordered-directory-contents/" rel="bookmark">Unordered Directory Contents</a>
|
||||
</h1>
|
||||
|
||||
<div class="entry-meta">
|
||||
<span class="date"><a href="http://indiestack.com/2017/09/unordered-directory-contents/" title="Permalink to Unordered Directory Contents" rel="bookmark"><time class="entry-date" datetime="2017-09-27T14:10:59+00:00">September 27, 2017</time></a></span><span class="categories-links"><a href="http://indiestack.com/category/cocoa/" rel="category tag">Cocoa</a>, <a href="http://indiestack.com/category/os-x/" rel="category tag">OS X</a></span><span class="author vcard"><a class="url fn n" href="http://indiestack.com/author/admin/" title="View all posts by Daniel Jalkut" rel="author">Daniel Jalkut</a></span> </div><!-- .entry-meta -->
|
||||
</header><!-- .entry-header -->
|
||||
|
||||
<div class="entry-content">
|
||||
<p>Since I updated to macOS 10.13 High Sierra, some of my unit tests broke. Examining the failures more carefully, I discovered that they were making assumptions about the order that Foundation’s FileManager.contentsOfDirectory(atPath:) would return items.</p>
|
||||
<p>I wrote a quick playground to test the behavior on a 10.12 machine:</p>
|
||||
<pre>
|
||||
import Foundation
|
||||
|
||||
let array = try! FileManager.default.contentsOfDirectory(atPath: "/Applications/Utilities")
|
||||
print("\(array.debugDescription)")
|
||||
</pre>
|
||||
<p>The results come back alphabetically ordered by file name:</p>
|
||||
<pre>
|
||||
[".DS_Store", ".localized", "Activity Monitor.app", "Adobe Flash Player Install Manager.app", "AirPort Utility.app", "Audio MIDI Setup.app", "Bluetooth File Exchange.app", "Boot Camp Assistant.app", "ColorSync Utility.app", "Console.app", "Digital Color Meter.app", "Disk Utility.app", "Grab.app", "Grapher.app", "Keychain Access.app", "Migration Assistant.app", "Script Editor.app", "System Information.app", "Terminal.app", "VoiceOver Utility.app"]
|
||||
</pre>
|
||||
<p>The same playground on 10.13 tells a different story:</p>
|
||||
<pre>
|
||||
["AirPort Utility.app", "VoiceOver Utility.app", "Terminal.app", "Activity Monitor.app", ".DS_Store", "Grapher.app", "Audio MIDI Setup.app", ".localized", "System Information.app", "Keychain Access.app", "Grab.app", "Migration Assistant.app", "Script Editor.app", "ColorSync Utility.app", "Console.app", "Disk Utility.app", "Bluetooth File Exchange.app", "Boot Camp Assistant.app", "Digital Color Meter.app"]
|
||||
</pre>
|
||||
<p>I thought at first this might have been related to the APFS conversion that 10.13 applied to my boot volume, but the same ordering discrepancy occurs for items on my HFS+ volumes as well.</p>
|
||||
<p>After checking the <a href="https://developer.apple.com/library/content/releasenotes/Foundation/RN-Foundation/index.html">10.13 release notes</a> for clues, and finding none, I consulted the <a href="https://developer.apple.com/documentation/foundation/nsfilemanager/1414584-contentsofdirectoryatpath?language=objc">documentation</a>. Well, what do you know?</p>
|
||||
<blockquote><p>The order of the files in the returned array is undefined.</p></blockquote>
|
||||
<p>So, mea culpa. The test code in question probably <em>shouldn’t</em> have ever made assumptions about the ordering of items returned from this method. While it has evidently always been <em>undefined</em>, it appears they are only making good on that promise in 10.13. You have been warned!</p>
|
||||
<p><strong>Update:</strong> It turns out I have some real bugs in my apps, not just in my tests, because of assuming the results of this call will be reasonably sorted. Luckily I use a bottleneck method for obtaining the list of files, and I can impose my own sorting right at the source. If you’re looking to make the same kinds of changes to your app, be sure to heed <a href="https://twitter.com/petermaurer/status/913109782378291207">Peter Maurer’s advice</a> and use “localizedStandardCompare” (available since macOS10.6/iOS4) to obtain Finder-like ordering of the results.</p>
|
||||
</div><!-- .entry-content -->
|
||||
|
||||
<footer class="entry-meta">
|
||||
|
||||
</footer><!-- .entry-meta -->
|
||||
</article><!-- #post -->
|
||||
|
||||
<article id="post-551" class="post-551 post type-post status-publish format-standard hentry category-apple category-bug-reports category-xcode">
|
||||
<header class="entry-header">
|
||||
|
||||
<h1 class="entry-title">
|
||||
<a href="http://indiestack.com/2017/09/sandbox-inheritance-tax/" rel="bookmark">Sandbox Inheritance Tax</a>
|
||||
</h1>
|
||||
|
||||
<div class="entry-meta">
|
||||
<span class="date"><a href="http://indiestack.com/2017/09/sandbox-inheritance-tax/" title="Permalink to Sandbox Inheritance Tax" rel="bookmark"><time class="entry-date" datetime="2017-09-25T17:21:30+00:00">September 25, 2017</time></a></span><span class="categories-links"><a href="http://indiestack.com/category/apple/" rel="category tag">Apple</a>, <a href="http://indiestack.com/category/bug-reports/" rel="category tag">Bug Reports</a>, <a href="http://indiestack.com/category/xcode/" rel="category tag">Xcode</a></span><span class="author vcard"><a class="url fn n" href="http://indiestack.com/author/admin/" title="View all posts by Daniel Jalkut" rel="author">Daniel Jalkut</a></span> </div><!-- .entry-meta -->
|
||||
</header><!-- .entry-header -->
|
||||
|
||||
<div class="entry-content">
|
||||
<p>I ran into a subtle bug with Xcode 9 that I think is worth sharing. Specifically, this bug affects Mac applications that:</p>
|
||||
<ol>
|
||||
<li>Are sandboxed.</li>
|
||||
<li>Launch a sandboxed subprocess with NSTask (or posix_spawn).</li>
|
||||
<li>Configure the subprocess to inherit the parent’s sandbox.</li>
|
||||
</ol>
|
||||
<p>When such an app is compiled with Xcode 9, the subprocess will crash whenever the parent process launches it. A canonical example of something that might suffer from this problem is a bundled crash-monitor. I embed one with my apps to keep an eye on the running status of the parent process, and to present a crash-reporting interface to users if the host app terminates prematurely. When I build and run my app with Xcode 9, the bundled crash monitor dies instantly upon being launched.</p>
|
||||
<p>It took me a while to realize that the subprocess is dying because it fails to satisfy the contract for inheriting a sandbox. From Apple’s “<a href="https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html#//apple_ref/doc/uid/TP40011195-CH4-SW15">Enabling App Sandbox Inheritance</a>“:</p>
|
||||
<blockquote><p>
|
||||
To enable sandbox inheritance, a child target must use exactly two App Sandbox entitlement keys: com.apple.security.app-sandbox and com.apple.security.inherit. If you specify any other App Sandbox entitlement, the system aborts the child process.
|
||||
</p></blockquote>
|
||||
<p>Well, that’s funny because my child process <em>does specify only those two keys</em>, but the system is aborting it anyway. It turns out that Xcode 9 is <em>inserting a third entitlement</em> without my permission. Clicking on the detail of the “Process Product Packaging” build phase in Xcode’s log navigator, I can see that there are <em>three entitlements</em> for my target:</p>
|
||||
<p><img src="http://indiestack.com/wp-content/uploads/2017/09/ProductPackaging.png" alt="Xcode build log detail showing the wrong entitlements." title="ProductPackaging.png" border="0" width="595" height="246" /></p>
|
||||
<p>When my subprocess is launched, the system sees that extra “com.apple.security.get-task-allow” entitlement in the context of “com.apple.security.inherit”, and unceremoniously crashes my the child process.</p>
|
||||
<p>I’m not sure what Apple’s reasoning is for imposing this entitlement on sandboxed targets, but it appears to be doing so across the board, for literally every sandboxed target in my app. I confirmed that all of my apps, XPC processes, helper tools, etc., are all getting this bonus entitlement.</p>
|
||||
<p>I searched Xcode’s files, and discovered the entitlement listed in this file inside the Xcode app bundle:</p>
|
||||
<pre>
|
||||
Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Xcode/PrivatePlugIns/IDEOSXSupportCore.ideplugin/Contents/Resources/BaseEntitlements.plist
|
||||
</pre>
|
||||
<p>Putting aside the question of whether it’s appropriate for Xcode to <em>surreptitiously</em> add entitlements that are not specified by the developer’s own list of permissions, the addition of the entitlement <em>for these particular targets</em>, ones that inherit their parent’s sandbox, turns out to be a fatal move.</p>
|
||||
<p>Ideally I would be able to work around this by adding a custom build phase to manually tweak the generated entitlements file, removing the unwanted key. But the “Process Product Packaging” build phase happens so late in the build process that it’s after the last user-specified custom build phase. There’s no room in Xcode’s current design for fixing up the problematic entitlements before they are incorporated into the signed product. As far as I can tell the only clean workaround would be to redundantly re-sign the child app with a custom script, and corrected entitlements, after Xcode’s build process is completed.</p>
|
||||
<p>I filed Radar #34628449, “Sandboxed project build with Xcode 9 cannot launch child process.”</p>
|
||||
<p><strong>Update:</strong> Colin Barrett <a href="https://twitter.com/cbarrett/status/912427986254692353">pointed out on Twitter</a> that the entitlement in question here, “com.apple.security.get-task-allow”, may be required in order to attach to and debug a process. If true, then I think this is something that was handled in a different way in Xcode 8. I can confirm that my apps do not have the entitlement imposed on them by Xcode 8, yet I am able to attach to and debug them.</p>
|
||||
<p>If Apple changed the debugger infrastructure in Xcode 9 so that the relationship between the debugger and target processes is more locked down, requiring a specific entitlement, then that’s probably a good thing. But if this change was made without thinking about the implications for the above-cited “strict two entitlement” rule for sandbox inheritance, then probably some flexibility needs to be applied to that rule.</p>
|
||||
<p>Finally, as I noted above the entitlement is being applied to <em>all my targets</em>. What I didn’t clarify is that the entitlement is added even when Building and Archiving. A release build’s binaries are endowed with this additional entitlement, which may also bring additional security vulnerabilities to the app.</p>
|
||||
<p>I would not ship a sandboxed Mac app that is built with Xcode 9, until we understand more about when Xcode applies this entitlement, and whether it can be prevented for Release builds at the very least.</p>
|
||||
<p><strong>Update 2:</strong> I’ve learned that Xcode’s “Export Archive” functionality causes the unwanted entitlement to be removed. Apparently the assumption is that everybody creates Xcode archives as part of their build and release process. I am sure this is true for most (all?) iOS deployments, but for Developer-ID signed apps on the Mac, there has traditionally been less of an incentive to do this. Got a properly signed Mac application? Zip it up, put it on a web server, and you’re done.</p>
|
||||
<p>I’m not sure yet whether I’ll switch my build process to use archiving, or whether I’ll pull some other stunt to redo the code signing with corrected entitlements. In any case this has been quite an adventure today getting to the bottom of this. I updated my bug report with Apple to request that they provide some standard build flag that would prevent the problematic entitlement from being added from the start. In the mean time, I’ll explore one of the workarounds and get my builds back to fully functional!</p>
|
||||
</div><!-- .entry-content -->
|
||||
|
||||
<footer class="entry-meta">
|
||||
|
||||
</footer><!-- .entry-meta -->
|
||||
</article><!-- #post -->
|
||||
|
||||
<article id="post-547" class="post-547 post type-post status-publish format-standard hentry category-bug-reports category-objc category-swift category-xcode">
|
||||
<header class="entry-header">
|
||||
|
||||
<h1 class="entry-title">
|
||||
<a href="http://indiestack.com/2017/09/better-swift-completion/" rel="bookmark">Better Swift Completion</a>
|
||||
</h1>
|
||||
|
||||
<div class="entry-meta">
|
||||
<span class="date"><a href="http://indiestack.com/2017/09/better-swift-completion/" title="Permalink to Better Swift Completion" rel="bookmark"><time class="entry-date" datetime="2017-09-22T11:33:18+00:00">September 22, 2017</time></a></span><span class="categories-links"><a href="http://indiestack.com/category/bug-reports/" rel="category tag">Bug Reports</a>, <a href="http://indiestack.com/category/objc/" rel="category tag">ObjC</a>, <a href="http://indiestack.com/category/swift/" rel="category tag">Swift</a>, <a href="http://indiestack.com/category/xcode/" rel="category tag">Xcode</a></span><span class="author vcard"><a class="url fn n" href="http://indiestack.com/author/admin/" title="View all posts by Daniel Jalkut" rel="author">Daniel Jalkut</a></span> </div><!-- .entry-meta -->
|
||||
</header><!-- .entry-header -->
|
||||
|
||||
<div class="entry-content">
|
||||
<p>Apple released <a href="https://developer.apple.com/xcode/">Xcode 9</a> earlier this week, and in spite of a few glitches here and there, I have found the update to be an overall improvement over Xcode 8. It’s nice that Apple continues to invest in the core tools for Mac and iOS developers.</p>
|
||||
<p>I’ve been dabbling in more and more Swift development lately, and it’s brought to light a shortcoming in Xcode’s code completion which has unfortunately <em>not improved</em> in Xcode 9: completion of Swift function calls when there is a large quantity of candidates.</p>
|
||||
<p>Take for example NSAttributedString. If I want to initialize a new instance in Swift, I type “NSAttributedString(” to bring up the list of compatible init methods I can choose from:</p>
|
||||
<p><img src="http://indiestack.com/wp-content/uploads/2017/09/SwiftCompletion-1.png" alt="SwiftCompletion" title="SwiftCompletion.png" border="0" width="452" height="225" /></p>
|
||||
<p>The problem at this point is that I have to navigate the menu by hand. I can’t narrow down the list of completions any further by typing, because the very next character I type will be interpreted as the manual filling out of parameters of the NSAttributedString initializer.</p>
|
||||
<p><img src="http://indiestack.com/wp-content/uploads/2017/09/BadCompletion-1.png" alt="BadCompletion" title="BadCompletion.png" border="0" width="437" height="232" /></p>
|
||||
<p>This is a situation where Objective-C gets much nicer treatment in the editor. Because completion in Objective-C begins when I start typing “init”, and because the named first parameter is <em>part of the init</em> message name, I can winnow down the results quite a bit:</p>
|
||||
<p><img src="http://indiestack.com/wp-content/uploads/2017/09/Pasted_Image_9_22_17__11_24_AM.png" alt="Pasted Image 9 22 17 11 24 AM" title="Pasted_Image_9_22_17__11_24_AM.png" border="0" width="438" height="146" /></p>
|
||||
<p>Better still, because Xcode performs a fuzzy match on the typing, I can proceed to type the names of <em>additional</em> parameters to zero in completely on the variation I want:</p>
|
||||
<p><img src="http://indiestack.com/wp-content/uploads/2017/09/MEAppController_AppDelegate_m_—_Edited.png" alt="MEAppController AppDelegate m Edited" title="MEAppController_AppDelegate_m_—_Edited.png" border="0" width="649" height="124" /></p>
|
||||
<p>When I accept the completion, all of my typing is replaced with the expected, templated parameter placeholders for the chose initializer.</p>
|
||||
<p>I filed Radar #34594940 requesting better completion for Swift.</p>
|
||||
</div><!-- .entry-content -->
|
||||
|
||||
<footer class="entry-meta">
|
||||
|
||||
</footer><!-- .entry-meta -->
|
||||
</article><!-- #post -->
|
||||
|
||||
<article id="post-534" class="post-534 post type-post status-publish format-standard hentry category-apple category-automation category-bug-reports category-os-x">
|
||||
<header class="entry-header">
|
||||
|
||||
<h1 class="entry-title">
|
||||
<a href="http://indiestack.com/2017/08/javascript-osa-handler-invocation/" rel="bookmark">JavaScript OSA Handler Invocation</a>
|
||||
</h1>
|
||||
|
||||
<div class="entry-meta">
|
||||
<span class="date"><a href="http://indiestack.com/2017/08/javascript-osa-handler-invocation/" title="Permalink to JavaScript OSA Handler Invocation" rel="bookmark"><time class="entry-date" datetime="2017-08-18T11:40:36+00:00">August 18, 2017</time></a></span><span class="categories-links"><a href="http://indiestack.com/category/apple/" rel="category tag">Apple</a>, <a href="http://indiestack.com/category/automation/" rel="category tag">Automation</a>, <a href="http://indiestack.com/category/bug-reports/" rel="category tag">Bug Reports</a>, <a href="http://indiestack.com/category/os-x/" rel="category tag">OS X</a></span><span class="author vcard"><a class="url fn n" href="http://indiestack.com/author/admin/" title="View all posts by Daniel Jalkut" rel="author">Daniel Jalkut</a></span> </div><!-- .entry-meta -->
|
||||
</header><!-- .entry-header -->
|
||||
|
||||
<div class="entry-content">
|
||||
<p>When Apple added support to macOS to support <a href="https://developer.apple.com/library/content/releasenotes/InterapplicationCommunication/RN-JavaScriptForAutomation/Articles/OSX10-10.html#//apple_ref/doc/uid/TP40014508-CH109-SW1">JavaScript for Automation</a>, they did so in a way that more or less allows folks who invoke AppleScripts to invoke JavaScript for Automation scripts as if they were exactly the same. An abstraction in Apple’s Open Script Architecture (OSA) makes it easy for script-running tools to theoretically handle any number of scripting languages without concern for the implementation details of those languages.</p>
|
||||
<p>This mostly works, but I recently received a bug report that shed light on a problem with Apple’s implementation of JavaScript with respect to <em>invoking a specific named handler.</em> The OSA provides a mechanism for loading and running a specific handler, or function, within a script. My app <a href="http://red-sweater.com/fastscripts/">FastScripts</a> takes advantage of this to query a script about whether it would prefer to be invoked in another process or not. Unfortunately, when it comes to JavaScript, Apple’s implementation <em>runs the whole script</em> in addition to running just the specific, named handler.</p>
|
||||
<p>If you’ve got Xcode handy, you can use this simple playground content to observe the problem:</p>
|
||||
<pre>
|
||||
import OSAKit
|
||||
|
||||
if let javaScriptLanguage = OSALanguage(forName: "JavaScript") {
|
||||
let scriptSource = "Application('Safari').activate();" +
|
||||
"function boo() { ObjC.import('Cocoa'); $.NSBeep(); }"
|
||||
let myScript = OSAScript(source: scriptSource, language: javaScriptLanguage)
|
||||
|
||||
// Only the behavior of boo should be observed
|
||||
myScript.executeHandler(withName: "boo", arguments: [], error: nil)
|
||||
}
|
||||
|
||||
// Give time for the beep to sound
|
||||
RunLoop.current.run(until: Date(timeIntervalSinceNow:5))
|
||||
</pre>
|
||||
<p>The named function “boo()” only invokes NSBeep, so when this playground is run, all that should happen is a beep should be emitted from the Mac. Instead, when it runs Safari becomes the active application. This is because in addition to running the “boo()” handler, it also runs the whole script at the top level.</p>
|
||||
<p>A workaround to the bug is to wrap the top level functionality of a script in a “run()” handler, so where the scriptSource is declared above, instead use:</p>
|
||||
<pre>
|
||||
let scriptSource = "function run() { Application('Safari').activate(); }" +
|
||||
"function boo() { ObjC.import('Cocoa'); $.NSBeep(); }"
|
||||
</pre>
|
||||
<p>I hope this helps the one other person on earth who cares about invoking JavaScript for Automation methods indvidually! (Radar #33962901, though I’m not holding my breath on this one!)</p>
|
||||
</div><!-- .entry-content -->
|
||||
|
||||
<footer class="entry-meta">
|
||||
|
||||
</footer><!-- .entry-meta -->
|
||||
</article><!-- #post -->
|
||||
|
||||
<article id="post-529" class="post-529 post type-post status-publish format-standard hentry category-apple category-bug-reports category-open-source category-xcode">
|
||||
<header class="entry-header">
|
||||
|
||||
<h1 class="entry-title">
|
||||
<a href="http://indiestack.com/2017/06/xcode-github-integration/" rel="bookmark">Xcode GitHub Integration</a>
|
||||
</h1>
|
||||
|
||||
<div class="entry-meta">
|
||||
<span class="date"><a href="http://indiestack.com/2017/06/xcode-github-integration/" title="Permalink to Xcode GitHub Integration" rel="bookmark"><time class="entry-date" datetime="2017-06-07T10:34:59+00:00">June 7, 2017</time></a></span><span class="categories-links"><a href="http://indiestack.com/category/apple/" rel="category tag">Apple</a>, <a href="http://indiestack.com/category/bug-reports/" rel="category tag">Bug Reports</a>, <a href="http://indiestack.com/category/open-source/" rel="category tag">Open Source</a>, <a href="http://indiestack.com/category/xcode/" rel="category tag">Xcode</a></span><span class="author vcard"><a class="url fn n" href="http://indiestack.com/author/admin/" title="View all posts by Daniel Jalkut" rel="author">Daniel Jalkut</a></span> </div><!-- .entry-meta -->
|
||||
</header><!-- .entry-header -->
|
||||
|
||||
<div class="entry-content">
|
||||
<p>Apple’s <a href="http://adcdownload.apple.com/WWDC_2017/Xcode_9_beta/Xcode_9_beta_Release_Notes.pdf">beta release of Xcode 9</a> features impressive improvements to its source control features, including streamlined integration with GitHub. There’s even a fancy “Open in Xcode” button when you go to clone a project:</p>
|
||||
<p><img src="http://indiestack.com/wp-content/uploads/2017/06/OpenInXcode.png" alt="Screen capture of the GitHub interface for cloning a project" title="OpenInXcode.png" border="0" width="432" height="254" /></p>
|
||||
<p>This integration is amazing. You just click the button, specify a save folder in Xcode, and boom! You’re off and …</p>
|
||||
<p><img src="http://indiestack.com/wp-content/uploads/2017/06/MacDevelopment.png" alt="Screen capture of build failure indicating a missing signing certificate" title="MacDevelopment.png" border="0" width="515" height="229" /></p>
|
||||
<p>Oh, right. Code signing. The otherwise stellar GitHub integration in Xcode underscores a longstanding deficiency in how it manages code signing identities for multi-team, collaborative projects. Precisely the kinds of projects you’re liable to find on GitHub.</p>
|
||||
<p>The problem could be solved, or at least diminished greatly, by providing some mechanism for declaring that a project should be code signed “with the user’s own default developer team.” The default branch of any open source project targeting Apple platforms, would specify the DEVELOPMENT_TEAM as something like:</p>
|
||||
<pre>
|
||||
DEVELOPMENT_TEAM = Automatic
|
||||
</pre>
|
||||
<p>Xcode would provide a user-level setting for “Default Development Team”, and in the absence of any overriding setting, <em>that team would be used</em> whenever a project was configured as above.</p>
|
||||
<p>I <a href="http://indiestack.com/2016/06/dynamic-development-team/">wrote about this problem</a> once before, but with all the work being put into streamlining the experience of cloning from and pushing to GitHub, now is an ideal time for Apple to embrace a fix. Radar #32614751.</p>
|
||||
<p>Another issue that stops short the cloning, and immediate building and running, of open source projects, is the need to fulfill external dependencies. In some cases this might require manually downloading and installing libraries, or cloning projects, but in the vast majority of cases the dependencies will be specified using built-in Git submodule support, or a popular package manager. In each of these cases, it should be trivial for Xcode to detect that the project it has just cloned also has dependencies:</p>
|
||||
<ul>
|
||||
<li><strong>Git submodules</strong>: there is a .gitmodules directory.</li>
|
||||
<li><strong>Carthage</strong>: there is a Cartfile file.</li>
|
||||
<li><strong>CocoaPods</strong>: there is a Podfile file.</li>
|
||||
<li><strong>Swift Package Manager</strong>: there is a Swift.package file.</li>
|
||||
</ul>
|
||||
<p>If Xcode sees evidence of any of these techniques at play, it could do the favor of checking them out immediately after cloning the project. Radar #32615265.</p>
|
||||
<p>The GitHub integration coming in Xcode 9 provides a nearly effortless capability for cloning, building, and running open source projects that target Apple platforms. Ideally it would also go the extra mile and provide for variable, dynamic development teams, as well as conduct a rudimentary check for dependencies that must be checked out before commencing work on the project.</p>
|
||||
</div><!-- .entry-content -->
|
||||
|
||||
<footer class="entry-meta">
|
||||
|
||||
</footer><!-- .entry-meta -->
|
||||
</article><!-- #post -->
|
||||
|
||||
<article id="post-523" class="post-523 post type-post status-publish format-standard hentry category-cocoa category-links category-mac">
|
||||
<header class="entry-header">
|
||||
|
||||
<h1 class="entry-title">
|
||||
<a href="http://indiestack.com/2017/06/evergreen-images/" rel="bookmark">Evergreen Images</a>
|
||||
</h1>
|
||||
|
||||
<div class="entry-meta">
|
||||
<span class="date"><a href="http://indiestack.com/2017/06/evergreen-images/" title="Permalink to Evergreen Images" rel="bookmark"><time class="entry-date" datetime="2017-06-01T12:47:35+00:00">June 1, 2017</time></a></span><span class="categories-links"><a href="http://indiestack.com/category/cocoa/" rel="category tag">Cocoa</a>, <a href="http://indiestack.com/category/links/" rel="category tag">Links</a>, <a href="http://indiestack.com/category/mac/" rel="category tag">Mac</a></span><span class="author vcard"><a class="url fn n" href="http://indiestack.com/author/admin/" title="View all posts by Daniel Jalkut" rel="author">Daniel Jalkut</a></span> </div><!-- .entry-meta -->
|
||||
</header><!-- .entry-header -->
|
||||
|
||||
<div class="entry-content">
|
||||
<p>Brent Simmons, the original developer of <a href="https://red-sweater.com/marsedit/">MarsEdit</a> and <a href="http://netnewswireapp.com">NetNewsWire</a>, is building a new feed reader app called <a href="http://ranchero.com/evergreen/">Evergreen</a>:</p>
|
||||
<blockquote><p>Evergreen is an open source, productivity-style feed reader for Macs.</p>
|
||||
<p>It’s at a very early stage — we use it, but we don’t expect other people to use it yet.</p></blockquote>
|
||||
<p>I’ve never been one to shy away from early-stage software, so of course I ran to the <a href="https://github.com/brentsimmons/Evergreen">GitHub project page</a>, cloned the repository, and built it immediately on my own Mac.</p>
|
||||
<p><img src="http://indiestack.com/wp-content/uploads/2017/06/EvergreenGeneric.png" alt="Screenshot of Evergreen about box without a custom icon." title="EvergreenGeneric.png" border="0" width="284" height="198" /></p>
|
||||
<p>Ahh, the tell-tale sign of a young app: the generic about box. Personally, I like to give apps-in-progress an icon, even if only a placeholder image, as soon as possible. It occurred to me that Apple has done the favor of providing a pretty-darned-suitable image for “Evergreen” in the form of its Emoji glyph of the same name:</p>
|
||||
<p><span style="font-size:3em;">🌲</span></p>
|
||||
<p>Since I have the source code right here, why don’t I render that tree at a large size in a graphics app, resize it to a million different resolutions, bundle it up and check it in to the Evergreen source base?</p>
|
||||
<p>Because that’s not nearly as fun as doing it in code. I dove into the Evergreen application delegate class, adding the <a href="https://github.com/brentsimmons/Evergreen/blob/master/Evergreen/AppDelegate.swift#L43-L66">following function</a>:</p>
|
||||
<pre>
|
||||
private func evergreenImage() -> NSImage? {
|
||||
var image: NSImage? = nil
|
||||
let imageWidth = 1024
|
||||
let imageHeight = 1024
|
||||
let imageSize = NSMakeSize(CGFloat(imageWidth), CGFloat(imageHeight))
|
||||
|
||||
if let drawingContext = CGContext(data: nil, width: imageWidth, height: imageHeight, bitsPerComponent: 8, bytesPerRow: 0, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue) {
|
||||
|
||||
let graphicsContext = NSGraphicsContext(cgContext: drawingContext, flipped: false)
|
||||
NSGraphicsContext.saveGraphicsState()
|
||||
NSGraphicsContext.setCurrent(graphicsContext)
|
||||
|
||||
let targetRect = NSRect(origin: NSZeroPoint, size: imageSize)
|
||||
NSString(string: "🌲").draw(in: targetRect, withAttributes: [NSFontAttributeName: NSFont.systemFont(ofSize: 1000)])
|
||||
|
||||
NSGraphicsContext.restoreGraphicsState()
|
||||
|
||||
if let coreImage = drawingContext.makeImage() {
|
||||
image = NSImage(cgImage: coreImage, size: imageSize)
|
||||
}
|
||||
}
|
||||
|
||||
return image
|
||||
}
|
||||
</pre>
|
||||
<p>In summary this code: creates a CoreGraphics drawing context, renders a huge evergreen Emoji glyph into it, and creates an NSImage out of it.</p>
|
||||
<p>Then from the “applicationDidFinishLaunching()” function:</p>
|
||||
<pre>
|
||||
if let appIconImage = evergreenImage() {
|
||||
appIconImage.setName("NSApplicationIcon")
|
||||
NSApplication.shared().applicationIconImage = appIconImage
|
||||
}
|
||||
</pre>
|
||||
<p>Give the newly created image the canonical name, used by AppKit, for looking up the application icon, and immediately change the application’s icon image to reflect the new value. It worked a treat:</p>
|
||||
<p><img src="http://indiestack.com/wp-content/uploads/2017/06/EvergreenEmoji.png" alt="EvergreenEmoji" title="EvergreenEmoji.png" border="0" width="284" height="198" /></p>
|
||||
<p>In programming there is usually a hard way, an easy way, <em>and a fun way</em>. Be sure to take the third option as often as possible.</p>
|
||||
</div><!-- .entry-content -->
|
||||
|
||||
<footer class="entry-meta">
|
||||
|
||||
</footer><!-- .entry-meta -->
|
||||
</article><!-- #post -->
|
||||
|
||||
<nav class="navigation paging-navigation" role="navigation">
|
||||
<h1 class="screen-reader-text">Posts navigation</h1>
|
||||
<div class="nav-links">
|
||||
|
||||
<div class="nav-previous"><a href="http://indiestack.com/page/2/" ><span class="meta-nav">←</span> Older posts</a></div>
|
||||
|
||||
|
||||
</div><!-- .nav-links -->
|
||||
</nav><!-- .navigation -->
|
||||
|
||||
|
||||
</div><!-- #content -->
|
||||
</div><!-- #primary -->
|
||||
|
||||
|
||||
</div><!-- #main -->
|
||||
<footer id="colophon" class="site-footer" role="contentinfo">
|
||||
<div id="secondary" class="sidebar-container" role="complementary">
|
||||
<div class="widget-area">
|
||||
<aside id="search-2" class="widget widget_search"><form role="search" method="get" class="search-form" action="http://indiestack.com/">
|
||||
<label>
|
||||
<span class="screen-reader-text">Search for:</span>
|
||||
<input type="search" class="search-field" placeholder="Search …" value="" name="s" />
|
||||
</label>
|
||||
<input type="submit" class="search-submit" value="Search" />
|
||||
</form></aside> <aside id="recent-posts-2" class="widget widget_recent_entries"> <h3 class="widget-title">Recent Posts</h3> <ul>
|
||||
<li>
|
||||
<a href="http://indiestack.com/2017/11/treat-warnings-as-errors-in-swift/">Treat Warnings as Errors in Swift</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://indiestack.com/2017/10/selective-selector-mapping/">Selective Selector Mapping</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://indiestack.com/2017/09/swatch-your-step/">Swatch Your Step</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://indiestack.com/2017/09/xcode-9-signing-workarounds/">Xcode 9 Signing Workarounds</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://indiestack.com/2017/09/unordered-directory-contents/">Unordered Directory Contents</a>
|
||||
</li>
|
||||
</ul>
|
||||
</aside> <aside id="archives-2" class="widget widget_archive"><h3 class="widget-title">Archives</h3> <ul>
|
||||
<li><a href='http://indiestack.com/2017/11/'>November 2017</a></li>
|
||||
<li><a href='http://indiestack.com/2017/10/'>October 2017</a></li>
|
||||
<li><a href='http://indiestack.com/2017/09/'>September 2017</a></li>
|
||||
<li><a href='http://indiestack.com/2017/08/'>August 2017</a></li>
|
||||
<li><a href='http://indiestack.com/2017/06/'>June 2017</a></li>
|
||||
<li><a href='http://indiestack.com/2017/05/'>May 2017</a></li>
|
||||
<li><a href='http://indiestack.com/2017/04/'>April 2017</a></li>
|
||||
<li><a href='http://indiestack.com/2017/03/'>March 2017</a></li>
|
||||
<li><a href='http://indiestack.com/2017/01/'>January 2017</a></li>
|
||||
<li><a href='http://indiestack.com/2016/12/'>December 2016</a></li>
|
||||
<li><a href='http://indiestack.com/2016/10/'>October 2016</a></li>
|
||||
<li><a href='http://indiestack.com/2016/08/'>August 2016</a></li>
|
||||
<li><a href='http://indiestack.com/2016/07/'>July 2016</a></li>
|
||||
<li><a href='http://indiestack.com/2016/06/'>June 2016</a></li>
|
||||
<li><a href='http://indiestack.com/2016/05/'>May 2016</a></li>
|
||||
<li><a href='http://indiestack.com/2016/04/'>April 2016</a></li>
|
||||
<li><a href='http://indiestack.com/2016/03/'>March 2016</a></li>
|
||||
<li><a href='http://indiestack.com/2016/02/'>February 2016</a></li>
|
||||
<li><a href='http://indiestack.com/2016/01/'>January 2016</a></li>
|
||||
<li><a href='http://indiestack.com/2015/09/'>September 2015</a></li>
|
||||
<li><a href='http://indiestack.com/2015/08/'>August 2015</a></li>
|
||||
<li><a href='http://indiestack.com/2015/07/'>July 2015</a></li>
|
||||
<li><a href='http://indiestack.com/2015/05/'>May 2015</a></li>
|
||||
<li><a href='http://indiestack.com/2015/04/'>April 2015</a></li>
|
||||
<li><a href='http://indiestack.com/2015/02/'>February 2015</a></li>
|
||||
<li><a href='http://indiestack.com/2015/01/'>January 2015</a></li>
|
||||
<li><a href='http://indiestack.com/2014/12/'>December 2014</a></li>
|
||||
<li><a href='http://indiestack.com/2014/11/'>November 2014</a></li>
|
||||
<li><a href='http://indiestack.com/2014/10/'>October 2014</a></li>
|
||||
<li><a href='http://indiestack.com/2014/09/'>September 2014</a></li>
|
||||
<li><a href='http://indiestack.com/2014/08/'>August 2014</a></li>
|
||||
<li><a href='http://indiestack.com/2014/05/'>May 2014</a></li>
|
||||
<li><a href='http://indiestack.com/2014/01/'>January 2014</a></li>
|
||||
<li><a href='http://indiestack.com/2013/12/'>December 2013</a></li>
|
||||
<li><a href='http://indiestack.com/2013/10/'>October 2013</a></li>
|
||||
<li><a href='http://indiestack.com/2013/09/'>September 2013</a></li>
|
||||
<li><a href='http://indiestack.com/2013/05/'>May 2013</a></li>
|
||||
</ul>
|
||||
</aside><aside id="categories-2" class="widget widget_categories"><h3 class="widget-title">Categories</h3> <ul>
|
||||
<li class="cat-item cat-item-11"><a href="http://indiestack.com/category/apple/" >Apple</a>
|
||||
</li>
|
||||
<li class="cat-item cat-item-6"><a href="http://indiestack.com/category/automation/" >Automation</a>
|
||||
</li>
|
||||
<li class="cat-item cat-item-17"><a href="http://indiestack.com/category/bug-reports/" >Bug Reports</a>
|
||||
</li>
|
||||
<li class="cat-item cat-item-8"><a href="http://indiestack.com/category/cocoa/" >Cocoa</a>
|
||||
</li>
|
||||
<li class="cat-item cat-item-14"><a href="http://indiestack.com/category/debugging/" >Debugging</a>
|
||||
</li>
|
||||
<li class="cat-item cat-item-21"><a href="http://indiestack.com/category/hacking/" >Hacking</a>
|
||||
</li>
|
||||
<li class="cat-item cat-item-18"><a href="http://indiestack.com/category/humor/" >Humor</a>
|
||||
</li>
|
||||
<li class="cat-item cat-item-3"><a href="http://indiestack.com/category/ios/" >iOS</a>
|
||||
</li>
|
||||
<li class="cat-item cat-item-10"><a href="http://indiestack.com/category/links/" >Links</a>
|
||||
</li>
|
||||
<li class="cat-item cat-item-5"><a href="http://indiestack.com/category/mac/" >Mac</a>
|
||||
</li>
|
||||
<li class="cat-item cat-item-23"><a href="http://indiestack.com/category/objc/" >ObjC</a>
|
||||
</li>
|
||||
<li class="cat-item cat-item-4"><a href="http://indiestack.com/category/open-source/" >Open Source</a>
|
||||
</li>
|
||||
<li class="cat-item cat-item-15"><a href="http://indiestack.com/category/os-x/" >OS X</a>
|
||||
</li>
|
||||
<li class="cat-item cat-item-19"><a href="http://indiestack.com/category/swift/" >Swift</a>
|
||||
</li>
|
||||
<li class="cat-item cat-item-22"><a href="http://indiestack.com/category/testing/" >Testing</a>
|
||||
</li>
|
||||
<li class="cat-item cat-item-25"><a href="http://indiestack.com/category/tips/" >Tips</a>
|
||||
</li>
|
||||
<li class="cat-item cat-item-20"><a href="http://indiestack.com/category/tvos/" >tvOS</a>
|
||||
</li>
|
||||
<li class="cat-item cat-item-1"><a href="http://indiestack.com/category/uncategorized/" >Uncategorized</a>
|
||||
</li>
|
||||
<li class="cat-item cat-item-24"><a href="http://indiestack.com/category/watchos/" >watchOS</a>
|
||||
</li>
|
||||
<li class="cat-item cat-item-16"><a href="http://indiestack.com/category/webkit/" >WebKit</a>
|
||||
</li>
|
||||
<li class="cat-item cat-item-13"><a href="http://indiestack.com/category/worfklow/" >Worfklow</a>
|
||||
</li>
|
||||
<li class="cat-item cat-item-9"><a href="http://indiestack.com/category/workflow/" >Workflow</a>
|
||||
</li>
|
||||
<li class="cat-item cat-item-7"><a href="http://indiestack.com/category/xcode/" >Xcode</a>
|
||||
</li>
|
||||
</ul>
|
||||
</aside><aside id="meta-2" class="widget widget_meta"><h3 class="widget-title">Meta</h3> <ul>
|
||||
<li><a href="http://indiestack.com/wp-login.php">Log in</a></li>
|
||||
<li><a href="http://indiestack.com/feed/">Entries <abbr title="Really Simple Syndication">RSS</abbr></a></li>
|
||||
<li><a href="http://indiestack.com/comments/feed/">Comments <abbr title="Really Simple Syndication">RSS</abbr></a></li>
|
||||
<li><a href="https://wordpress.org/" title="Powered by WordPress, state-of-the-art semantic personal publishing platform.">WordPress.org</a></li> </ul>
|
||||
</aside> </div><!-- .widget-area -->
|
||||
</div><!-- #secondary -->
|
||||
|
||||
<div class="site-info">
|
||||
<a href="https://wordpress.org/">Proudly powered by WordPress</a>
|
||||
</div><!-- .site-info -->
|
||||
</footer><!-- #colophon -->
|
||||
</div><!-- #page -->
|
||||
|
||||
<script type='text/javascript' src='http://indiestack.com/wp-includes/js/imagesloaded.min.js?ver=3.2.0'></script>
|
||||
<script type='text/javascript' src='http://indiestack.com/wp-includes/js/masonry.min.js?ver=3.3.2'></script>
|
||||
<script type='text/javascript' src='http://indiestack.com/wp-includes/js/jquery/jquery.masonry.min.js?ver=3.1.2b'></script>
|
||||
<script type='text/javascript' src='http://indiestack.com/wp-content/themes/twentythirteen/js/functions.js?ver=20160717'></script>
|
||||
<script type='text/javascript' src='http://indiestack.com/wp-includes/js/wp-embed.min.js?ver=4.8.3'></script>
|
||||
<script> jQuery(function($){ $(".widget_meta a[href='http://indiestack.com/comments/feed/']").parent().remove(); }); </script></body>
|
||||
</html>
|
||||
<!-- Performance optimized by W3 Total Cache. Learn more: https://www.w3-edge.com/products/
|
||||
|
||||
Page Caching using disk: enhanced
|
||||
|
||||
Served from: indiestack.com @ 2017-11-25 18:04:31 by W3 Total Cache -->
|
@ -1,172 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>inessential: weblog</title>
|
||||
<meta name="MSSmartTagsPreventParsing" content="true" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<style type="text/css" media="all">@import "http://inessential.com/styles/styleSheet.css";</style>
|
||||
<script type="text/javascript"> </script> <!-- FOUC hack: http://www.bluerobot.com/web/css/fouc.asp -->
|
||||
<link rel="alternate" type="application/rss+xml" title="RSS" href="http://inessential.com/xml/rss.xml" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="banner"><div id="innerbanner"><span id="biglink"><a href="http://inessential.com/"><img src="http://inessential.com/images/inessential_logo@2x.png" height=57 width=263 alt="inessential.com" /></a></span> <span id="bigbyline">by Brent Simmons</span></div>
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
|
||||
<div class="weblogPost">
|
||||
<h3><a href="http://inessential.com/2016/03/04/lizcast">Lizcast</a></h3>
|
||||
<div class="weblogPostBody"><p>The Omni Group’s Liz Marley, who recently transitioned from testing to engineering, <a href="https://overcast.fm/+BO9vrOfGc">appears on the NSNorth 2016 podcast</a>. She talks about…</p>
|
||||
|
||||
<blockquote><p>…challenges in engineering school, working with office cats, making the transition from software engineering to testing to developing and how knitting, like code, has the ultimate undo.</p></blockquote>
|
||||
|
||||
<p>Knitting is serious (though not somber) business here at Omni.</p></div>
|
||||
<div class="weblogPostDateline"><span class="weblogPostDisplayDate"><a href="http://inessential.com/2016/03/04/lizcast">04 Mar 2016</a></span></div>
|
||||
</div><div class="weblogPost">
|
||||
<h3><a href="http://inessential.com/2016/03/01/omnioutliner_4_5">OmniOutliner 4.5</a></h3>
|
||||
<div class="weblogPostBody"><p><a href="https://www.omnigroup.com/omnioutliner">OmniOutliner 4.5</a> is up on Omni’s site, and should be in the Mac App Store within days.</p>
|
||||
|
||||
<p>With this release — <a href="https://www.omnigroup.com/releasenotes/omnioutliner-mac">see the release notes</a> — I helped work on, of all things, <em>printing</em> bugs and features. This is the first time in my entire career where I worked on printing support that was more than just the most basic possible thing.</p>
|
||||
|
||||
<p>And that sounds weird for the year 2016, I realize. But here’s the thing: working on printing support is far from glamorous. You wouldn’t call it <em>fun</em>. But the people who need these features really do need them, and it’s a matter of respect for OmniOutliner users that we do a great job even with printing.</p>
|
||||
|
||||
<p>But I sure am glad to get it finished and shipping. And I’m proud of the work we did — more proud than I expected to be. It’s solid, and I think the people who print from OmniOutliner will be very pleased.</p>
|
||||
|
||||
<p>Now we’re on to <a href="https://www.omnigroup.com/blog/looking-back-looking-ahead-2016-edition">other new features</a>, including editing Markdown documents with OmniOutliner.</p></div>
|
||||
<div class="weblogPostDateline"><span class="weblogPostDisplayDate"><a href="http://inessential.com/2016/03/01/omnioutliner_4_5">01 Mar 2016</a></span></div>
|
||||
</div><div class="weblogPost">
|
||||
<h3><a href="http://inessential.com/2016/02/25/omnidev">OmniDev</a></h3>
|
||||
<div class="weblogPostBody"><p><a href="https://www.omnigroup.com/jobs/#mac-and-ios-developer">Omni is hiring a Mac/iOS developer</a>!</p>
|
||||
|
||||
<p>We’re also hiring a <a href="https://www.omnigroup.com/jobs/#senior-front-end-web-developer">web developer</a>, <a href="https://www.omnigroup.com/jobs/#graphic-designer">graphic designer</a>, and <a href="https://www.omnigroup.com/jobs/#phone-support-human">phone support humans</a>.</p>
|
||||
|
||||
<p>I’ll let you try out my new beanbag chair.</p></div>
|
||||
<div class="weblogPostDateline"><span class="weblogPostDisplayDate"><a href="http://inessential.com/2016/02/25/omnidev">25 Feb 2016</a></span></div>
|
||||
</div><div class="weblogPost">
|
||||
<h3><a href="http://inessential.com/2016/02/18/omnijobs">OmniJobs</a></h3>
|
||||
<div class="weblogPostBody"><p><a href="https://www.omnigroup.com/jobs">We’re hiring</a> a senior front-end web developer, a graphic designer, and support humans.</p>
|
||||
|
||||
<p>You should apply.</p></div>
|
||||
<div class="weblogPostDateline"><span class="weblogPostDisplayDate"><a href="http://inessential.com/2016/02/18/omnijobs">18 Feb 2016</a></span></div>
|
||||
</div><div class="weblogPost">
|
||||
<h3><a href="http://inessential.com/2016/02/11/it_will_be_trump">It Will Be Trump</a></h3>
|
||||
<div class="weblogPostBody"><p>The South Carolina primary is where the establishment fixes the errors of Iowa and New Hampshire. It’s Lee Atwater’s firewall.</p>
|
||||
|
||||
<p>When Buchanan threatens Dole, South Carolina shuts it down. When McCain threatens Bush, South Carolina applies the kibosh.</p>
|
||||
|
||||
<p>But is there any hope that it will function that way this time?</p>
|
||||
|
||||
<p>I don’t think so. The establishment candidates are Bush, Rubio, and Kasich. They don’t have a shot. Nor does Cruz. Trump wins South Carolina.</p>
|
||||
|
||||
<p>If that’s true, then it’s all over. If South Carolina fails — if the very primary that’s designed to toss the ball back to the establishment fails — then there’s no hope at all.</p>
|
||||
|
||||
<p>Cruz will go on to win a few states, most notably Texas. But otherwise it’s going to be Trump. He’ll get the delegates he needs, and that will be that.</p></div>
|
||||
<div class="weblogPostDateline"><span class="weblogPostDisplayDate"><a href="http://inessential.com/2016/02/11/it_will_be_trump">11 Feb 2016</a></span></div>
|
||||
</div><div class="weblogPost">
|
||||
<h3><a href="http://inessential.com/2016/02/09/origin_of_good_and_bad_hair_day">Origin of Good (and Bad) Hair Day</a></h3>
|
||||
<div class="weblogPostBody"><p>When I was in middle school in the late ’70s I struggled to get my hair to feather properly. It just didn’t want to do it.</p>
|
||||
|
||||
<p>Like many kids that age I was newly conscious of my appearance — and I naïvely thought that well-feathered hair was a necessary (though not sufficient) key to fitting in. (Which was probably true, by the way.)</p>
|
||||
|
||||
<p>Every morning I would find that my hair behaved, at least somewhat, or it didn’t. So I categorized each day as a “good hair day” and a “bad hair day.”</p>
|
||||
|
||||
<p>I told my friends about this categorization — including a neighborhood girl named Sarah. She ended up telling other kids at school.</p>
|
||||
|
||||
<p>And pretty soon those kids, even kids I didn’t really know, would stop me in the halls or at lunch and say, “Hey Brent — good hair day or bad hair day?” Not meanly. Teasingly. It was funny.</p>
|
||||
|
||||
<p>Years later I started hearing the phrase on TV, and I was surprised that my little middle-school thing had spread and become <a href="http://www.ecenglish.com/learnenglish/lessons/why-do-we-say-bad-hair-day">part of the culture</a>.</p>
|
||||
|
||||
<p style="text-align:center">* * *</p>
|
||||
|
||||
|
||||
<p>Of course, it’s also possible that I <a href="http://www.word-detective.com/072104.html">picked it up from Jane Pauley</a>. But for all these years I’ve believed — no joke — that it was me, that it was my phrase. Maybe Jane Pauley got it (indirectly) from me.</p>
|
||||
|
||||
<p>It’s <em>highly</em> unlikely — of course, I know this — that I’m the originator. But still, it had to be someone, right?</p>
|
||||
|
||||
<p>(Not necessarily. It’s kind of obvious and could have had many originators.)</p>
|
||||
|
||||
<p style="text-align:center">* * *</p>
|
||||
|
||||
|
||||
<p>I stopped categorizing good and bad hair days by the time I got to high school. And these days I’m just glad that I still have some hair.</p></div>
|
||||
<div class="weblogPostDateline"><span class="weblogPostDisplayDate"><a href="http://inessential.com/2016/02/09/origin_of_good_and_bad_hair_day">09 Feb 2016</a></span></div>
|
||||
</div><div class="weblogPost">
|
||||
<h3><a href="http://inessential.com/2016/02/08/river5">River5</a></h3>
|
||||
<div class="weblogPostBody"><p>River5 is Dave Winer’s <a href="https://github.com/scripting/river5">river-of-news RSS aggregator</a>.</p>
|
||||
|
||||
<p>It’s a Node app. You can run it on a public machine and access it anywhere, or run it on your desktop and just read your news there.</p></div>
|
||||
<div class="weblogPostDateline"><span class="weblogPostDisplayDate"><a href="http://inessential.com/2016/02/08/river5">08 Feb 2016</a></span></div>
|
||||
</div><div class="weblogPost">
|
||||
<h3><a href="http://inessential.com/2016/02/06/stop_watch">Stop Watch</a></h3>
|
||||
<div class="weblogPostBody"><p>Some time last week my iPhone started prompting me frequently to re-enter my iCloud password. And then my Watch started doing the same, about once a minute — with a little tap on the wrist each time.</p>
|
||||
|
||||
<p>Obviously I <em>did</em> re-enter my password — and have done so a dozen or so times now — but it doesn’t seem to matter.</p>
|
||||
|
||||
<p>So I stopped wearing my Watch and have switched to a mid-sixties Hamilton that my Dad gave me. (He had gotten it as a high school graduation present.)</p>
|
||||
|
||||
<p>I’m no watch aficionado — but I do appreciate a good and attractive watch (which this is), and I appreciate even more an old watch that’s a family thing.</p>
|
||||
|
||||
<p>Here’s the thing, though: the Apple Watch contains a hundred miracles of engineering and design, surely, but serious problems with software and services can turn even the most incredible hardware into something you just sit on your desk and ignore.</p></div>
|
||||
<div class="weblogPostDateline"><span class="weblogPostDisplayDate"><a href="http://inessential.com/2016/02/06/stop_watch">06 Feb 2016</a></span></div>
|
||||
</div><div class="weblogPost">
|
||||
<h3><a href="http://inessential.com/2016/02/05/on_sanders_governing">On Sanders Governing</a></h3>
|
||||
<div class="weblogPostBody"><p>The Atlantic, <a href="http://www.theatlantic.com/politics/archive/2016/02/why-bernie-sanders-cant-win-and-cant-govern/460182/">Norm Ornstein</a>:</p>
|
||||
|
||||
<blockquote><p>But is there any real evidence that there is a hidden “sleeper cell” of potential voters who are waiting for the signal to emerge and transform the electorate? No.</p></blockquote>
|
||||
|
||||
<p>Pure candidates on both sides of the spectrum often claim that their purity will bring in the checked-out voters, because they’re just waiting for a <em>real</em> conservative or a <em>real</em> liberal.</p>
|
||||
|
||||
<p>It’s an enduring fairy tale with terrible consequences. To put faith in it is to lose to the other party.</p></div>
|
||||
<div class="weblogPostDateline"><span class="weblogPostDisplayDate"><a href="http://inessential.com/2016/02/05/on_sanders_governing">05 Feb 2016</a></span></div>
|
||||
</div><div class="weblogPost">
|
||||
<h3><a href="http://inessential.com/2016/02/05/cocoaconf_podcast_with_me">CocoaConf Podcast with Me</a></h3>
|
||||
<div class="weblogPostBody"><p>Cesare Rocchi interviewed me for the latest <a href="http://cocoaconf.com/podcast/16">CocoaConf Podcast</a> on life before the App Store.</p>
|
||||
|
||||
<p>There <em>was</em> a life, by the way. It was fun! We could release software any time we wanted to.</p></div>
|
||||
<div class="weblogPostDateline"><span class="weblogPostDisplayDate"><a href="http://inessential.com/2016/02/05/cocoaconf_podcast_with_me">05 Feb 2016</a></span></div>
|
||||
</div>
|
||||
<p><a href="http://inessential.com/archive">Archive</a></p>
|
||||
|
||||
</div> <!-- content -->
|
||||
|
||||
<div id="deckad">
|
||||
<div id="innerdeckad">
|
||||
<span id="adsViaTheDeck"><a href="http://decknetwork.net/">Ads via The Deck</a></span>
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
(function(id) {
|
||||
document.write('<script type="text/javascript" src="' +
|
||||
'http://connect.decknetwork.net/deck' + id + '_js.php?' +
|
||||
(new Date().getTime()) + '"></' + 'script>');
|
||||
})("IE");
|
||||
//]]>
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="sidebar">
|
||||
|
||||
|
||||
<!-- <p class="sidebarText"><a id="vesperlink" href="http://vesperapp.co/appstore">Vesper</a> - iOS app • <a href="http://therecord.co/">The Record</a> - podcast</p> -->
|
||||
<!-- <p class="sidebarText">Twitter: <a href="https://twitter.com/brentsimmons">brentsimmons</a> • <a href="https://twitter.com/inessential">inessential</a></p> -->
|
||||
<p class="sidebarText">What I work on at Omni<br /><a href="http://www.omnigroup.com/omnifocus">OmniFocus for Mac</a> • <a href="http://www.omnigroup.com/omnioutliner">OmniOutliner for Mac</a></p>
|
||||
<!-- <p class="sidebarText"> Open source<br /><a href="https://github.com/quartermaster/QSKit">Q Branch Standard Kit</a> •
|
||||
<a href="https://github.com/quartermaster/DB5">DB5</a></p> -->
|
||||
<p class="sidebarText"><a href="http://inessential.com/swiftdiary">Swift Diary</a><br />
|
||||
<a href="http://inessential.com/vespersyncdiary">Vesper Sync Diary</a> • <a href="http://inessential.com/hownottocrash">How Not to Crash</a><br />
|
||||
<a href="http://inessential.com/apps_ive_made">Apps I’ve Made</a> • <a href="http://inessential.com/xml/rss.xml">RSS</a></p>
|
||||
|
||||
|
||||
|
||||
</div> <!-- sidebar -->
|
||||
|
||||
<div id="footer">
|
||||
<p>© 1999-2016 Brent Simmons</p>
|
||||
<p>Made in Seattle. Go Hawks!</p>
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
File diff suppressed because it is too large
Load Diff
@ -1,18 +0,0 @@
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
|
||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/../Frameworks @loader_path/../Frameworks
|
||||
INFOPLIST_FILE = FeedFinderTests/Info.plist
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.ranchero.FeedFinderTests
|
||||
PRODUCT_NAME = $(TARGET_NAME)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,58 +0,0 @@
|
||||
CODE_SIGN_IDENTITY =
|
||||
CODE_SIGN_STYLE = Automatic
|
||||
DEVELOPMENT_TEAM = M8L2WTLA8W
|
||||
|
||||
// See the notes in Evergreen_target.xcconfig on why the
|
||||
// DeveloperSettings.xcconfig is #included here
|
||||
|
||||
#include? "../../../SharedXcodeSettings/DeveloperSettings.xcconfig"
|
||||
|
||||
SDKROOT = macosx
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13
|
||||
CLANG_ENABLE_OBJC_WEAK = YES
|
||||
SWIFT_VERSION = 4.0
|
||||
COMBINE_HIDPI_IMAGES = YES
|
||||
|
||||
COPY_PHASE_STRIP = NO
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13
|
||||
ALWAYS_SEARCH_USER_PATHS = NO
|
||||
CURRENT_PROJECT_VERSION = 1
|
||||
VERSION_INFO_PREFIX =
|
||||
VERSIONING_SYSTEM = apple-generic
|
||||
GCC_NO_COMMON_BLOCKS = YES
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99
|
||||
CLANG_CXX_LANGUAGE_STANDARD = gnu++0x
|
||||
CLANG_CXX_LIBRARY = libc++
|
||||
CLANG_ENABLE_MODULES = YES
|
||||
CLANG_ENABLE_OBJC_ARC = YES
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES
|
||||
CLANG_WARN_EMPTY_BODY = YES
|
||||
CLANG_WARN_BOOL_CONVERSION = YES
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES
|
||||
CLANG_WARN_ENUM_CONVERSION = YES
|
||||
CLANG_WARN_INT_CONVERSION = YES
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES
|
||||
CLANG_WARN_INFINITE_RECURSION = YES
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES
|
||||
CLANG_WARN_COMMA = YES
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES
|
||||
GCC_WARN_UNUSED_FUNCTION = YES
|
||||
GCC_WARN_UNUSED_VARIABLE = YES
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
|
||||
CLANG_ANALYZER_NONNULL = YES
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = Off
|
@ -1,15 +0,0 @@
|
||||
#include "./FeedFinder_project.xcconfig"
|
||||
|
||||
DEBUG_INFORMATION_FORMAT = dwarf
|
||||
ENABLE_TESTABILITY = YES
|
||||
GCC_DYNAMIC_NO_PIC = NO
|
||||
GCC_OPTIMIZATION_LEVEL = 0
|
||||
GCC_PREPROCESSOR_DEFINITIONS = DEBUG=1 $(inherited)
|
||||
|
||||
MTL_ENABLE_DEBUG_INFO = YES
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG
|
||||
SWIFT_COMPILATION_MODE = singlefile
|
||||
SWIFT_OPTIMIZATION_LEVEL = -Onone
|
||||
ONLY_ACTIVE_ARCH = YES
|
||||
|
||||
|
@ -1,9 +0,0 @@
|
||||
#include "./FeedFinder_project.xcconfig"
|
||||
|
||||
DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
|
||||
ENABLE_NS_ASSERTIONS = NO
|
||||
|
||||
MTL_ENABLE_DEBUG_INFO = NO
|
||||
SWIFT_OPTIMIZATION_LEVEL = -O
|
||||
|
||||
SWIFT_COMPILATION_MODE = wholemodule
|
@ -1,14 +0,0 @@
|
||||
|
||||
INSTALL_PATH = $(LOCAL_LIBRARY_DIR)/Frameworks
|
||||
SKIP_INSTALL = YES
|
||||
DYLIB_COMPATIBILITY_VERSION = 1
|
||||
DYLIB_CURRENT_VERSION = 1
|
||||
DYLIB_INSTALL_NAME_BASE = @rpath
|
||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/../Frameworks @loader_path/Frameworks
|
||||
DEFINES_MODULE = YES
|
||||
FRAMEWORK_VERSION = A
|
||||
INFOPLIST_FILE = FeedFinder/Info.plist
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.ranchero.FeedFinder
|
||||
PRODUCT_NAME = $(TARGET_NAME)
|
||||
|
||||
|
@ -10,4 +10,6 @@ To add a submodule:
|
||||
|
||||
To update a submodule — to get the latest changes:
|
||||
|
||||
TBD I surely wish I knew how
|
||||
git submodule update
|
||||
|
||||
I think. Not sure about the above.
|
||||
|
Loading…
x
Reference in New Issue
Block a user