From 72a89dd048cbbaa6e00f9911941bb9d513c327e2 Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Mon, 18 Jun 2018 16:52:24 -0700 Subject: [PATCH] Break the build. Continue switch to submodules. Remove local copy of RSParser. --- Evergreen.xcodeproj/project.pbxproj | 60 - Frameworks/RSParser/Feeds/FeedParser.h | 24 - Frameworks/RSParser/Feeds/FeedParser.swift | 88 - .../RSParser/Feeds/FeedParserError.swift | 29 - Frameworks/RSParser/Feeds/FeedType.swift | 61 - .../RSParser/Feeds/JSON/JSONFeedParser.swift | 230 -- .../RSParser/Feeds/JSON/RSSInJSONParser.swift | 180 - .../RSParser/Feeds/ParsedAttachment.swift | 38 - Frameworks/RSParser/Feeds/ParsedAuthor.swift | 38 - Frameworks/RSParser/Feeds/ParsedFeed.swift | 42 - Frameworks/RSParser/Feeds/ParsedHub.swift | 28 - Frameworks/RSParser/Feeds/ParsedItem.swift | 58 - .../RSParser/Feeds/XML/AtomParser.swift | 28 - Frameworks/RSParser/Feeds/XML/RSAtomParser.h | 18 - Frameworks/RSParser/Feeds/XML/RSAtomParser.m | 635 ---- .../RSParser/Feeds/XML/RSParsedArticle.h | 36 - .../RSParser/Feeds/XML/RSParsedArticle.m | 131 - .../RSParser/Feeds/XML/RSParsedAuthor.h | 19 - .../RSParser/Feeds/XML/RSParsedAuthor.m | 33 - .../RSParser/Feeds/XML/RSParsedEnclosure.h | 22 - .../RSParser/Feeds/XML/RSParsedEnclosure.m | 13 - Frameworks/RSParser/Feeds/XML/RSParsedFeed.h | 22 - Frameworks/RSParser/Feeds/XML/RSParsedFeed.m | 29 - .../Feeds/XML/RSParsedFeedTransformer.swift | 76 - Frameworks/RSParser/Feeds/XML/RSRSSParser.h | 19 - Frameworks/RSParser/Feeds/XML/RSRSSParser.m | 473 --- Frameworks/RSParser/Feeds/XML/RSSParser.swift | 28 - Frameworks/RSParser/HTML/RSHTMLLinkParser.h | 31 - Frameworks/RSParser/HTML/RSHTMLLinkParser.m | 151 - Frameworks/RSParser/HTML/RSHTMLMetadata.h | 84 - Frameworks/RSParser/HTML/RSHTMLMetadata.m | 430 --- .../RSParser/HTML/RSHTMLMetadataParser.h | 24 - .../RSParser/HTML/RSHTMLMetadataParser.m | 142 - Frameworks/RSParser/HTML/RSHTMLTag.h | 29 - Frameworks/RSParser/HTML/RSHTMLTag.m | 39 - Frameworks/RSParser/HTML/RSSAXHTMLParser.h | 49 - Frameworks/RSParser/HTML/RSSAXHTMLParser.m | 315 -- Frameworks/RSParser/Info.plist | 26 - Frameworks/RSParser/JSON/JSONTypes.swift | 12 - Frameworks/RSParser/JSON/JSONUtilities.swift | 27 - Frameworks/RSParser/LICENSE | 21 - Frameworks/RSParser/OPML/RSOPMLAttributes.h | 36 - Frameworks/RSParser/OPML/RSOPMLAttributes.m | 66 - Frameworks/RSParser/OPML/RSOPMLDocument.h | 17 - Frameworks/RSParser/OPML/RSOPMLDocument.m | 13 - Frameworks/RSParser/OPML/RSOPMLError.h | 19 - Frameworks/RSParser/OPML/RSOPMLError.m | 22 - .../RSParser/OPML/RSOPMLFeedSpecifier.h | 24 - .../RSParser/OPML/RSOPMLFeedSpecifier.m | 50 - Frameworks/RSParser/OPML/RSOPMLItem.h | 30 - Frameworks/RSParser/OPML/RSOPMLItem.m | 86 - Frameworks/RSParser/OPML/RSOPMLParser.h | 26 - Frameworks/RSParser/OPML/RSOPMLParser.m | 301 -- Frameworks/RSParser/ParserData.h | 23 - Frameworks/RSParser/ParserData.m | 26 - Frameworks/RSParser/README.md | 73 - Frameworks/RSParser/RSParser.h | 54 - .../RSParser.xcodeproj/project.pbxproj | 791 ----- .../contents.xcworkspacedata | 7 - .../RSParserTests/AtomParserTests.swift | 110 - .../RSParserTests/EntityDecodingTests.swift | 23 - .../RSParserTests/FeedParserTypeTests.swift | 213 -- .../RSParserTests/HTMLLinkTests.swift | 42 - .../RSParserTests/HTMLMetadataTests.swift | 141 - Frameworks/RSParser/RSParserTests/Info.plist | 22 - .../RSParserTests/JSONFeedParserTests.swift | 95 - .../RSParser/RSParserTests/OPMLTests.swift | 66 - .../RSParserTests/RSDateParserTests.m | 98 - .../RSParserTests/RSSInJSONParserTests.swift | 22 - .../RSParserTests/RSSParserTests.swift | 113 - .../RSParserTests/Resources/4fsodonline.atom | 1 - .../Resources/DaringFireball.atom | 1821 ---------- .../Resources/DaringFireball.html | 1341 ------- .../Resources/DaringFireball.json | 584 --- .../Resources/DaringFireball.rss | 2278 ------------ .../RSParserTests/Resources/EMarley.rss | 97 - .../RSParserTests/Resources/KatieFloyd.rss | 352 -- .../Resources/OneFootTsunami.atom | 673 ---- .../Resources/ScriptingNews.json | 945 ----- .../RSParserTests/Resources/Subs.opml | 223 -- .../Resources/allthis-partial.json | 1 - .../RSParserTests/Resources/allthis.atom | 520 --- .../RSParserTests/Resources/allthis.json | 1 - .../RSParserTests/Resources/coco.html | 2329 ------------ .../RSParserTests/Resources/curt.json | 1 - .../RSParserTests/Resources/dcrainmaker.xml | 1475 -------- .../Resources/expertopinionent.atom | 930 ----- .../RSParserTests/Resources/furbo.html | 372 -- .../RSParserTests/Resources/inessential.html | 172 - .../RSParserTests/Resources/inessential.json | 156 - .../RSParserTests/Resources/macworld.rss | 3123 ----------------- .../RSParserTests/Resources/manton.rss | 228 -- .../RSParserTests/Resources/natasha.xml | 1066 ------ .../RSParserTests/Resources/pxlnv.json | 249 -- .../RSParserTests/Resources/scriptingNews.rss | 443 --- .../RSParserTests/Resources/sixcolors.html | 1102 ------ .../RSParserTests/Resources/theomnishow.rss | 135 - Frameworks/RSParser/SAX/RSSAXParser.h | 68 - Frameworks/RSParser/SAX/RSSAXParser.m | 347 -- .../RSParser/Utilities/NSData+RSParser.h | 26 - .../RSParser/Utilities/NSData+RSParser.m | 146 - .../RSParser/Utilities/NSString+RSParser.h | 23 - .../RSParser/Utilities/NSString+RSParser.m | 432 --- Frameworks/RSParser/Utilities/RSDateParser.h | 22 - Frameworks/RSParser/Utilities/RSDateParser.m | 435 --- .../RSParser/Utilities/RSParserInternal.h | 24 - .../RSParser/Utilities/RSParserInternal.m | 60 - .../xcconfig/RSParserTests_target.xcconfig | 18 - .../xcconfig/RSParser_project.xcconfig | 84 - .../xcconfig/RSParser_project_debug.xcconfig | 14 - .../RSParser_project_release.xcconfig | 9 - .../xcconfig/RSParser_target.xcconfig | 15 - 112 files changed, 28788 deletions(-) delete mode 100755 Frameworks/RSParser/Feeds/FeedParser.h delete mode 100644 Frameworks/RSParser/Feeds/FeedParser.swift delete mode 100644 Frameworks/RSParser/Feeds/FeedParserError.swift delete mode 100644 Frameworks/RSParser/Feeds/FeedType.swift delete mode 100644 Frameworks/RSParser/Feeds/JSON/JSONFeedParser.swift delete mode 100644 Frameworks/RSParser/Feeds/JSON/RSSInJSONParser.swift delete mode 100644 Frameworks/RSParser/Feeds/ParsedAttachment.swift delete mode 100644 Frameworks/RSParser/Feeds/ParsedAuthor.swift delete mode 100644 Frameworks/RSParser/Feeds/ParsedFeed.swift delete mode 100644 Frameworks/RSParser/Feeds/ParsedHub.swift delete mode 100644 Frameworks/RSParser/Feeds/ParsedItem.swift delete mode 100644 Frameworks/RSParser/Feeds/XML/AtomParser.swift delete mode 100755 Frameworks/RSParser/Feeds/XML/RSAtomParser.h delete mode 100755 Frameworks/RSParser/Feeds/XML/RSAtomParser.m delete mode 100755 Frameworks/RSParser/Feeds/XML/RSParsedArticle.h delete mode 100755 Frameworks/RSParser/Feeds/XML/RSParsedArticle.m delete mode 100644 Frameworks/RSParser/Feeds/XML/RSParsedAuthor.h delete mode 100644 Frameworks/RSParser/Feeds/XML/RSParsedAuthor.m delete mode 100644 Frameworks/RSParser/Feeds/XML/RSParsedEnclosure.h delete mode 100644 Frameworks/RSParser/Feeds/XML/RSParsedEnclosure.m delete mode 100755 Frameworks/RSParser/Feeds/XML/RSParsedFeed.h delete mode 100755 Frameworks/RSParser/Feeds/XML/RSParsedFeed.m delete mode 100644 Frameworks/RSParser/Feeds/XML/RSParsedFeedTransformer.swift delete mode 100755 Frameworks/RSParser/Feeds/XML/RSRSSParser.h delete mode 100755 Frameworks/RSParser/Feeds/XML/RSRSSParser.m delete mode 100644 Frameworks/RSParser/Feeds/XML/RSSParser.swift delete mode 100755 Frameworks/RSParser/HTML/RSHTMLLinkParser.h delete mode 100755 Frameworks/RSParser/HTML/RSHTMLLinkParser.m delete mode 100755 Frameworks/RSParser/HTML/RSHTMLMetadata.h delete mode 100755 Frameworks/RSParser/HTML/RSHTMLMetadata.m delete mode 100755 Frameworks/RSParser/HTML/RSHTMLMetadataParser.h delete mode 100755 Frameworks/RSParser/HTML/RSHTMLMetadataParser.m delete mode 100644 Frameworks/RSParser/HTML/RSHTMLTag.h delete mode 100644 Frameworks/RSParser/HTML/RSHTMLTag.m delete mode 100755 Frameworks/RSParser/HTML/RSSAXHTMLParser.h delete mode 100755 Frameworks/RSParser/HTML/RSSAXHTMLParser.m delete mode 100644 Frameworks/RSParser/Info.plist delete mode 100644 Frameworks/RSParser/JSON/JSONTypes.swift delete mode 100644 Frameworks/RSParser/JSON/JSONUtilities.swift delete mode 100644 Frameworks/RSParser/LICENSE delete mode 100755 Frameworks/RSParser/OPML/RSOPMLAttributes.h delete mode 100755 Frameworks/RSParser/OPML/RSOPMLAttributes.m delete mode 100755 Frameworks/RSParser/OPML/RSOPMLDocument.h delete mode 100755 Frameworks/RSParser/OPML/RSOPMLDocument.m delete mode 100755 Frameworks/RSParser/OPML/RSOPMLError.h delete mode 100755 Frameworks/RSParser/OPML/RSOPMLError.m delete mode 100755 Frameworks/RSParser/OPML/RSOPMLFeedSpecifier.h delete mode 100755 Frameworks/RSParser/OPML/RSOPMLFeedSpecifier.m delete mode 100755 Frameworks/RSParser/OPML/RSOPMLItem.h delete mode 100755 Frameworks/RSParser/OPML/RSOPMLItem.m delete mode 100755 Frameworks/RSParser/OPML/RSOPMLParser.h delete mode 100755 Frameworks/RSParser/OPML/RSOPMLParser.m delete mode 100644 Frameworks/RSParser/ParserData.h delete mode 100644 Frameworks/RSParser/ParserData.m delete mode 100644 Frameworks/RSParser/README.md delete mode 100644 Frameworks/RSParser/RSParser.h delete mode 100644 Frameworks/RSParser/RSParser.xcodeproj/project.pbxproj delete mode 100644 Frameworks/RSParser/RSParser.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 Frameworks/RSParser/RSParserTests/AtomParserTests.swift delete mode 100644 Frameworks/RSParser/RSParserTests/EntityDecodingTests.swift delete mode 100644 Frameworks/RSParser/RSParserTests/FeedParserTypeTests.swift delete mode 100644 Frameworks/RSParser/RSParserTests/HTMLLinkTests.swift delete mode 100644 Frameworks/RSParser/RSParserTests/HTMLMetadataTests.swift delete mode 100644 Frameworks/RSParser/RSParserTests/Info.plist delete mode 100644 Frameworks/RSParser/RSParserTests/JSONFeedParserTests.swift delete mode 100644 Frameworks/RSParser/RSParserTests/OPMLTests.swift delete mode 100755 Frameworks/RSParser/RSParserTests/RSDateParserTests.m delete mode 100644 Frameworks/RSParser/RSParserTests/RSSInJSONParserTests.swift delete mode 100644 Frameworks/RSParser/RSParserTests/RSSParserTests.swift delete mode 100644 Frameworks/RSParser/RSParserTests/Resources/4fsodonline.atom delete mode 100644 Frameworks/RSParser/RSParserTests/Resources/DaringFireball.atom delete mode 100755 Frameworks/RSParser/RSParserTests/Resources/DaringFireball.html delete mode 100644 Frameworks/RSParser/RSParserTests/Resources/DaringFireball.json delete mode 100755 Frameworks/RSParser/RSParserTests/Resources/DaringFireball.rss delete mode 100755 Frameworks/RSParser/RSParserTests/Resources/EMarley.rss delete mode 100755 Frameworks/RSParser/RSParserTests/Resources/KatieFloyd.rss delete mode 100755 Frameworks/RSParser/RSParserTests/Resources/OneFootTsunami.atom delete mode 100644 Frameworks/RSParser/RSParserTests/Resources/ScriptingNews.json delete mode 100755 Frameworks/RSParser/RSParserTests/Resources/Subs.opml delete mode 100644 Frameworks/RSParser/RSParserTests/Resources/allthis-partial.json delete mode 100644 Frameworks/RSParser/RSParserTests/Resources/allthis.atom delete mode 100644 Frameworks/RSParser/RSParserTests/Resources/allthis.json delete mode 100644 Frameworks/RSParser/RSParserTests/Resources/coco.html delete mode 100644 Frameworks/RSParser/RSParserTests/Resources/curt.json delete mode 100644 Frameworks/RSParser/RSParserTests/Resources/dcrainmaker.xml delete mode 100644 Frameworks/RSParser/RSParserTests/Resources/expertopinionent.atom delete mode 100755 Frameworks/RSParser/RSParserTests/Resources/furbo.html delete mode 100755 Frameworks/RSParser/RSParserTests/Resources/inessential.html delete mode 100644 Frameworks/RSParser/RSParserTests/Resources/inessential.json delete mode 100644 Frameworks/RSParser/RSParserTests/Resources/macworld.rss delete mode 100755 Frameworks/RSParser/RSParserTests/Resources/manton.rss delete mode 100644 Frameworks/RSParser/RSParserTests/Resources/natasha.xml delete mode 100644 Frameworks/RSParser/RSParserTests/Resources/pxlnv.json delete mode 100644 Frameworks/RSParser/RSParserTests/Resources/scriptingNews.rss delete mode 100755 Frameworks/RSParser/RSParserTests/Resources/sixcolors.html delete mode 100644 Frameworks/RSParser/RSParserTests/Resources/theomnishow.rss delete mode 100755 Frameworks/RSParser/SAX/RSSAXParser.h delete mode 100755 Frameworks/RSParser/SAX/RSSAXParser.m delete mode 100644 Frameworks/RSParser/Utilities/NSData+RSParser.h delete mode 100644 Frameworks/RSParser/Utilities/NSData+RSParser.m delete mode 100755 Frameworks/RSParser/Utilities/NSString+RSParser.h delete mode 100755 Frameworks/RSParser/Utilities/NSString+RSParser.m delete mode 100755 Frameworks/RSParser/Utilities/RSDateParser.h delete mode 100755 Frameworks/RSParser/Utilities/RSDateParser.m delete mode 100755 Frameworks/RSParser/Utilities/RSParserInternal.h delete mode 100755 Frameworks/RSParser/Utilities/RSParserInternal.m delete mode 100644 Frameworks/RSParser/xcconfig/RSParserTests_target.xcconfig delete mode 100644 Frameworks/RSParser/xcconfig/RSParser_project.xcconfig delete mode 100644 Frameworks/RSParser/xcconfig/RSParser_project_debug.xcconfig delete mode 100644 Frameworks/RSParser/xcconfig/RSParser_project_release.xcconfig delete mode 100644 Frameworks/RSParser/xcconfig/RSParser_target.xcconfig diff --git a/Evergreen.xcodeproj/project.pbxproj b/Evergreen.xcodeproj/project.pbxproj index 79fb0ef3a..95cb9df38 100644 --- a/Evergreen.xcodeproj/project.pbxproj +++ b/Evergreen.xcodeproj/project.pbxproj @@ -93,8 +93,6 @@ 849A97A91ED9F9AA007D329B /* AddFeedSheet.xib in Resources */ = {isa = PBXBuildFile; fileRef = 849A97A71ED9F9AA007D329B /* AddFeedSheet.xib */; }; 849A97AC1ED9F9BC007D329B /* AddFolderSheet.xib in Resources */ = {isa = PBXBuildFile; fileRef = 849A97AA1ED9F9BC007D329B /* AddFolderSheet.xib */; }; 849A97B21ED9FA69007D329B /* MainWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 849A97B01ED9FA69007D329B /* MainWindow.storyboard */; }; - 849B897C1F0349D000578A8D /* RSParser.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 849B89751F0349C100578A8D /* RSParser.framework */; }; - 849B897D1F0349D000578A8D /* RSParser.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 849B89751F0349C100578A8D /* RSParser.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 849C64641ED37A5D003D8FC0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849C64631ED37A5D003D8FC0 /* AppDelegate.swift */; }; 849C64681ED37A5D003D8FC0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 849C64671ED37A5D003D8FC0 /* Assets.xcassets */; }; 849C646B1ED37A5D003D8FC0 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 849C64691ED37A5D003D8FC0 /* Main.storyboard */; }; @@ -253,27 +251,6 @@ remoteGlobalIDString = 844BEE361F0AB3AA004AB7CD; remoteInfo = Database; }; - 849B89741F0349C100578A8D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 849B89681F0349C100578A8D /* RSParser.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 84FF5F841EFA285800C15A01; - remoteInfo = RSParser; - }; - 849B89761F0349C100578A8D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 849B89681F0349C100578A8D /* RSParser.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 84FF5F8D1EFA285800C15A01; - remoteInfo = RSParserTests; - }; - 849B897E1F0349D000578A8D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 849B89681F0349C100578A8D /* RSParser.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = 84FF5F831EFA285800C15A01; - remoteInfo = RSParser; - }; 849C64721ED37A5D003D8FC0 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 849C64581ED37A5D003D8FC0 /* Project object */; @@ -383,7 +360,6 @@ files = ( 84BB4B781F11753300858766 /* Data.framework in Embed Frameworks */, 84FB9A301EDCD6C4003D53B9 /* Sparkle.framework in Embed Frameworks */, - 849B897D1F0349D000578A8D /* RSParser.framework in Embed Frameworks */, 846E77421F6EF6A100A165E2 /* Database.framework in Embed Frameworks */, 84B06FB31ED37DBD00F0B54B /* RSDatabase.framework in Embed Frameworks */, 84B06FEA1ED3803A00F0B54B /* RSFeedFinder.framework in Embed Frameworks */, @@ -514,7 +490,6 @@ 849A97A81ED9F9AA007D329B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Evergreen/Base.lproj/AddFeedSheet.xib; sourceTree = SOURCE_ROOT; }; 849A97AB1ED9F9BC007D329B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Evergreen/Base.lproj/AddFolderSheet.xib; sourceTree = SOURCE_ROOT; }; 849A97B11ED9FA69007D329B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Evergreen/Base.lproj/MainWindow.storyboard; sourceTree = SOURCE_ROOT; }; - 849B89681F0349C100578A8D /* RSParser.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSParser.xcodeproj; path = Frameworks/RSParser/RSParser.xcodeproj; sourceTree = ""; }; 849C64601ED37A5D003D8FC0 /* Evergreen.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Evergreen.app; sourceTree = BUILT_PRODUCTS_DIR; }; 849C64631ED37A5D003D8FC0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = Evergreen/AppDelegate.swift; sourceTree = ""; }; 849C64671ED37A5D003D8FC0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Evergreen/Assets.xcassets; sourceTree = ""; }; @@ -638,7 +613,6 @@ files = ( 84BB4B771F11753300858766 /* Data.framework in Frameworks */, 84B06FB21ED37DBD00F0B54B /* RSDatabase.framework in Frameworks */, - 849B897C1F0349D000578A8D /* RSParser.framework in Frameworks */, 846E77411F6EF6A100A165E2 /* Database.framework in Frameworks */, 84B06FE91ED3803A00F0B54B /* RSFeedFinder.framework in Frameworks */, 84B06FCF1ED37F7D00F0B54B /* DB5.framework in Frameworks */, @@ -983,15 +957,6 @@ path = Evergreen/Extensions; sourceTree = ""; }; - 849B89691F0349C100578A8D /* Products */ = { - isa = PBXGroup; - children = ( - 849B89751F0349C100578A8D /* RSParser.framework */, - 849B89771F0349C100578A8D /* RSParserTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; 849C64571ED37A5D003D8FC0 = { isa = PBXGroup; children = ( @@ -1038,7 +1003,6 @@ 84B06FC61ED37F7200F0B54B /* DB5.xcodeproj */, 84B06F961ED37DA000F0B54B /* RSDatabase.xcodeproj */, 84B06FE01ED3803200F0B54B /* RSFeedFinder.xcodeproj */, - 849B89681F0349C100578A8D /* RSParser.xcodeproj */, ); sourceTree = ""; }; @@ -1332,7 +1296,6 @@ 84B06FB51ED37DBD00F0B54B /* PBXTargetDependency */, 84B06FD21ED37F7D00F0B54B /* PBXTargetDependency */, 84B06FEC1ED3803A00F0B54B /* PBXTargetDependency */, - 849B897F1F0349D000578A8D /* PBXTargetDependency */, 84BB4B7A1F11753300858766 /* PBXTargetDependency */, 846E77401F6EF67A00A165E2 /* PBXTargetDependency */, 846E77441F6EF6A100A165E2 /* PBXTargetDependency */, @@ -1437,10 +1400,6 @@ ProductGroup = 84B06FE11ED3803200F0B54B /* Products */; ProjectRef = 84B06FE01ED3803200F0B54B /* RSFeedFinder.xcodeproj */; }, - { - ProductGroup = 849B89691F0349C100578A8D /* Products */; - ProjectRef = 849B89681F0349C100578A8D /* RSParser.xcodeproj */; - }, ); projectRoot = ""; targets = ( @@ -1482,20 +1441,6 @@ remoteRef = 846E773B1F6EF5D700A165E2 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 849B89751F0349C100578A8D /* RSParser.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = RSParser.framework; - remoteRef = 849B89741F0349C100578A8D /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 849B89771F0349C100578A8D /* RSParserTests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = RSParserTests.xctest; - remoteRef = 849B89761F0349C100578A8D /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; 84B06F9D1ED37DA000F0B54B /* RSDatabase.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; @@ -1830,11 +1775,6 @@ name = Database; targetProxy = 846E77431F6EF6A100A165E2 /* PBXContainerItemProxy */; }; - 849B897F1F0349D000578A8D /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = RSParser; - targetProxy = 849B897E1F0349D000578A8D /* PBXContainerItemProxy */; - }; 849C64731ED37A5D003D8FC0 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 849C645F1ED37A5D003D8FC0 /* Evergreen */; diff --git a/Frameworks/RSParser/Feeds/FeedParser.h b/Frameworks/RSParser/Feeds/FeedParser.h deleted file mode 100755 index 0f8df6b07..000000000 --- a/Frameworks/RSParser/Feeds/FeedParser.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// FeedParser.h -// RSXML -// -// Created by Brent Simmons on 7/12/15. -// Copyright © 2015 Ranchero Software, LLC. All rights reserved. -// - -@import Foundation; - -@class RSParsedFeed; -@class RSXMLData; - - -@protocol FeedParser - -+ (BOOL)canParseFeed:(RSXMLData * _Nonnull)xmlData; - -- (nonnull instancetype)initWithXMLData:(RSXMLData * _Nonnull)xmlData; - -- (nullable RSParsedFeed *)parseFeed:(NSError * _Nullable * _Nullable)error; - - -@end diff --git a/Frameworks/RSParser/Feeds/FeedParser.swift b/Frameworks/RSParser/Feeds/FeedParser.swift deleted file mode 100644 index 264837d94..000000000 --- a/Frameworks/RSParser/Feeds/FeedParser.swift +++ /dev/null @@ -1,88 +0,0 @@ -// -// FeedParser.swift -// RSParser -// -// Created by Brent Simmons on 6/20/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -// FeedParser handles RSS, Atom, JSON Feed, and RSS-in-JSON. -// You don’t need to know the type of feed. - -public typealias FeedParserCallback = (_ parsedFeed: ParsedFeed?, _ error: Error?) -> Void - -public struct FeedParser { - - public static func canParse(_ parserData: ParserData) -> Bool { - - let type = feedType(parserData) - - switch type { - case .jsonFeed, .rssInJSON, .rss, .atom: - return true - default: - return false - } - } - - public static func mightBeAbleToParseBasedOnPartialData(_ parserData: ParserData) -> Bool { - - let type = feedType(parserData, isPartialData: true) - - switch type { - case .jsonFeed, .rssInJSON, .rss, .atom, .unknown: - return true - default: - return false - } - } - - public static func parse(_ parserData: ParserData) throws -> ParsedFeed? { - - // This is generally fast enough to call on the main thread — - // but it’s probably a good idea to use a background queue if - // you might be doing a lot of parsing. (Such as in a feed reader.) - - do { - let type = feedType(parserData) - - switch type { - - case .jsonFeed: - return try JSONFeedParser.parse(parserData) - - case .rssInJSON: - return try RSSInJSONParser.parse(parserData) - - case .rss: - return RSSParser.parse(parserData) - - case .atom: - return AtomParser.parse(parserData) - - case .unknown, .notAFeed: - return nil - } - } - catch { throw error } - } - - public static func parse(_ parserData: ParserData, _ callback: @escaping FeedParserCallback) { - - DispatchQueue.global(qos: .background).async { - do { - let parsedFeed = try parse(parserData) - DispatchQueue.main.async { - callback(parsedFeed, nil) - } - } - catch { - DispatchQueue.main.async { - callback(nil, error) - } - } - } - } -} diff --git a/Frameworks/RSParser/Feeds/FeedParserError.swift b/Frameworks/RSParser/Feeds/FeedParserError.swift deleted file mode 100644 index 83ccbd059..000000000 --- a/Frameworks/RSParser/Feeds/FeedParserError.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// FeedParserError.swift -// RSParser -// -// Created by Brent Simmons on 6/24/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -public struct FeedParserError: Error { - - public enum FeedParserErrorType { - - case rssChannelNotFound - case rssItemsNotFound - case jsonFeedVersionNotFound - case jsonFeedItemsNotFound - case jsonFeedTitleNotFound - case invalidJSON - } - - public let errorType: FeedParserErrorType - - public init(_ errorType: FeedParserErrorType) { - - self.errorType = errorType - } -} diff --git a/Frameworks/RSParser/Feeds/FeedType.swift b/Frameworks/RSParser/Feeds/FeedType.swift deleted file mode 100644 index 16cefbea2..000000000 --- a/Frameworks/RSParser/Feeds/FeedType.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// FeedType.swift -// RSParser -// -// Created by Brent Simmons on 6/20/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -public enum FeedType { - case rss - case atom - case jsonFeed - case rssInJSON - case unknown - case notAFeed -} - - -private let minNumberOfBytesRequired = 128 - -public func feedType(_ parserData: ParserData, isPartialData: Bool = false) -> FeedType { - - // Can call with partial data — while still downloading, for instance. - // If there’s not enough data, return .unknown. Ask again when there’s more data. - // If it’s definitely not a feed, return .notAFeed. - // - // This is fast enough to call on the main thread. - - if parserData.data.count < minNumberOfBytesRequired { - return .unknown - } - - let nsdata = parserData.data as NSData - - if nsdata.isProbablyJSONFeed() { - return .jsonFeed - } - if nsdata.isProbablyRSSInJSON() { - return .rssInJSON - } - if nsdata.isProbablyRSS() { - return .rss - } - if nsdata.isProbablyAtom() { - return .atom - } - - if isPartialData && nsdata.isProbablyJSON() { - // Might not be able to detect a JSON Feed without all data. - // Dr. Drang’s JSON Feed (see althis.json and allthis-partial.json in tests) - // has, at this writing, the JSON version element at the end of the feed, - // which is totally legal — but it means not being able to detect - // that it’s a JSON Feed without all the data. - // So this returns .unknown instead of .notAFeed. - return .unknown - } - - return .notAFeed -} diff --git a/Frameworks/RSParser/Feeds/JSON/JSONFeedParser.swift b/Frameworks/RSParser/Feeds/JSON/JSONFeedParser.swift deleted file mode 100644 index 3f3bbf293..000000000 --- a/Frameworks/RSParser/Feeds/JSON/JSONFeedParser.swift +++ /dev/null @@ -1,230 +0,0 @@ -// -// JSONFeedParser.swift -// RSParser -// -// Created by Brent Simmons on 6/25/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -// See https://jsonfeed.org/version/1 - -public struct JSONFeedParser { - - struct Key { - static let version = "version" - static let items = "items" - static let title = "title" - static let homePageURL = "home_page_url" - static let feedURL = "feed_url" - static let feedDescription = "description" - static let nextURL = "next_url" - static let icon = "icon" - static let favicon = "favicon" - static let expired = "expired" - static let author = "author" - static let name = "name" - static let url = "url" - static let avatar = "avatar" - static let hubs = "hubs" - static let type = "type" - static let contentHTML = "content_html" - static let contentText = "content_text" - static let externalURL = "external_url" - static let summary = "summary" - static let image = "image" - static let bannerImage = "banner_image" - static let datePublished = "date_published" - static let dateModified = "date_modified" - static let tags = "tags" - static let uniqueID = "id" - static let attachments = "attachments" - static let mimeType = "mime_type" - static let sizeInBytes = "size_in_bytes" - static let durationInSeconds = "duration_in_seconds" - } - - static let jsonFeedVersionMarker = "://jsonfeed.org/version/" // Allow for the mistake of not getting the scheme exactly correct. - - public static func parse(_ parserData: ParserData) throws -> ParsedFeed? { - - guard let d = JSONUtilities.dictionary(with: parserData.data) else { - throw FeedParserError(.invalidJSON) - } - - guard let version = d[Key.version] as? String, let _ = version.range(of: JSONFeedParser.jsonFeedVersionMarker) else { - throw FeedParserError(.jsonFeedVersionNotFound) - } - guard let itemsArray = d[Key.items] as? JSONArray else { - throw FeedParserError(.jsonFeedItemsNotFound) - } - guard let title = d[Key.title] as? String else { - throw FeedParserError(.jsonFeedTitleNotFound) - } - - let authors = parseAuthors(d) - let homePageURL = d[Key.homePageURL] as? String - let feedURL = d[Key.feedURL] as? String ?? parserData.url - let feedDescription = d[Key.feedDescription] as? String - let nextURL = d[Key.nextURL] as? String - let iconURL = d[Key.icon] as? String - let faviconURL = d[Key.favicon] as? String - let expired = d[Key.expired] as? Bool ?? false - let hubs = parseHubs(d) - - let items = parseItems(itemsArray, parserData.url) - - return ParsedFeed(type: .jsonFeed, title: title, homePageURL: homePageURL, feedURL: feedURL, feedDescription: feedDescription, nextURL: nextURL, iconURL: iconURL, faviconURL: faviconURL, authors: authors, expired: expired, hubs: hubs, items: items) - } -} - -private extension JSONFeedParser { - - static func parseAuthors(_ dictionary: JSONDictionary) -> Set? { - - guard let authorDictionary = dictionary[Key.author] as? JSONDictionary else { - return nil - } - - let name = authorDictionary[Key.name] as? String - let url = authorDictionary[Key.url] as? String - let avatar = authorDictionary[Key.avatar] as? String - if name == nil && url == nil && avatar == nil { - return nil - } - let parsedAuthor = ParsedAuthor(name: name, url: url, avatarURL: avatar, emailAddress: nil) - return Set([parsedAuthor]) - } - - static func parseHubs(_ dictionary: JSONDictionary) -> Set? { - - guard let hubsArray = dictionary[Key.hubs] as? JSONArray else { - return nil - } - - let hubs = hubsArray.compactMap { (hubDictionary) -> ParsedHub? in - guard let hubURL = hubDictionary[Key.url] as? String, let hubType = hubDictionary[Key.type] as? String else { - return nil - } - return ParsedHub(type: hubType, url: hubURL) - } - return hubs.isEmpty ? nil : Set(hubs) - } - - static func parseItems(_ itemsArray: JSONArray, _ feedURL: String) -> Set { - - return Set(itemsArray.compactMap { (oneItemDictionary) -> ParsedItem? in - return parseItem(oneItemDictionary, feedURL) - }) - } - - static func parseItem(_ itemDictionary: JSONDictionary, _ feedURL: String) -> ParsedItem? { - - guard let uniqueID = parseUniqueID(itemDictionary) else { - return nil - } - - let contentHTML = itemDictionary[Key.contentHTML] as? String - let contentText = itemDictionary[Key.contentText] as? String - if contentHTML == nil && contentText == nil { - return nil - } - let decodedContentHTML = contentHTML?.rsparser_stringByDecodingHTMLEntities() - - let url = itemDictionary[Key.url] as? String - let externalURL = itemDictionary[Key.externalURL] as? String - let title = parseTitle(itemDictionary, feedURL) - let summary = itemDictionary[Key.summary] as? String - let imageURL = itemDictionary[Key.image] as? String - let bannerImageURL = itemDictionary[Key.bannerImage] as? String - - let datePublished = parseDate(itemDictionary[Key.datePublished] as? String) - let dateModified = parseDate(itemDictionary[Key.dateModified] as? String) - - let authors = parseAuthors(itemDictionary) - var tags: Set? = nil - if let tagsArray = itemDictionary[Key.tags] as? [String] { - tags = Set(tagsArray) - } - let attachments = parseAttachments(itemDictionary) - - return ParsedItem(syncServiceID: nil, uniqueID: uniqueID, feedURL: feedURL, url: url, externalURL: externalURL, title: title, contentHTML: decodedContentHTML, contentText: contentText, summary: summary, imageURL: imageURL, bannerImageURL: bannerImageURL, datePublished: datePublished, dateModified: dateModified, authors: authors, tags: tags, attachments: attachments) - } - - static func parseTitle(_ itemDictionary: JSONDictionary, _ feedURL: String) -> String? { - - guard let title = itemDictionary[Key.title] as? String else { - return nil - } - - if isSpecialCaseTitleWithEntitiesFeed(feedURL) { - return (title as NSString).rsparser_stringByDecodingHTMLEntities() - } - - return title - } - - static func isSpecialCaseTitleWithEntitiesFeed(_ feedURL: String) -> Bool { - - // As of 16 Feb. 2018, Kottke’s and Heer’s feeds includes HTML entities in the title elements. - // If we find more feeds like this, we’ll add them here. If these feeds get fixed, we’ll remove them. - - let lowerFeedURL = feedURL.lowercased() - let matchStrings = ["kottke.org", "pxlnv.com", "macstories.net"] - for matchString in matchStrings { - if lowerFeedURL.contains(matchString) { - return true - } - } - - return false - } - - static func parseUniqueID(_ itemDictionary: JSONDictionary) -> String? { - - if let uniqueID = itemDictionary[Key.uniqueID] as? String { - return uniqueID // Spec says it must be a string - } - // Spec also says that if it’s a number, even though that’s incorrect, it should be coerced to a string. - if let uniqueID = itemDictionary[Key.uniqueID] as? Int { - return "\(uniqueID)" - } - if let uniqueID = itemDictionary[Key.uniqueID] as? Double { - return "\(uniqueID)" - } - return nil - } - - static func parseDate(_ dateString: String?) -> Date? { - - guard let dateString = dateString, !dateString.isEmpty else { - return nil - } - return RSDateWithString(dateString) - } - - static func parseAttachments(_ itemDictionary: JSONDictionary) -> Set? { - - guard let attachmentsArray = itemDictionary[Key.attachments] as? JSONArray else { - return nil - } - return Set(attachmentsArray.compactMap { parseAttachment($0) }) - } - - static func parseAttachment(_ attachmentObject: JSONDictionary) -> ParsedAttachment? { - - guard let url = attachmentObject[Key.url] as? String else { - return nil - } - guard let mimeType = attachmentObject[Key.mimeType] as? String else { - return nil - } - - let title = attachmentObject[Key.title] as? String - let sizeInBytes = attachmentObject[Key.sizeInBytes] as? Int - let durationInSeconds = attachmentObject[Key.durationInSeconds] as? Int - - return ParsedAttachment(url: url, mimeType: mimeType, title: title, sizeInBytes: sizeInBytes, durationInSeconds: durationInSeconds) - } -} diff --git a/Frameworks/RSParser/Feeds/JSON/RSSInJSONParser.swift b/Frameworks/RSParser/Feeds/JSON/RSSInJSONParser.swift deleted file mode 100644 index 2bf595d47..000000000 --- a/Frameworks/RSParser/Feeds/JSON/RSSInJSONParser.swift +++ /dev/null @@ -1,180 +0,0 @@ -// -// RSSInJSONParser.swift -// RSParser -// -// Created by Brent Simmons on 6/24/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -// See https://github.com/scripting/Scripting-News/blob/master/rss-in-json/README.md -// Also: http://cyber.harvard.edu/rss/rss.html - -public struct RSSInJSONParser { - - public static func parse(_ parserData: ParserData) throws -> ParsedFeed? { - - do { - guard let parsedObject = try JSONSerialization.jsonObject(with: parserData.data) as? JSONDictionary else { - throw FeedParserError(.invalidJSON) - } - guard let rssObject = parsedObject["rss"] as? JSONDictionary else { - throw FeedParserError(.rssChannelNotFound) - } - guard let channelObject = rssObject["channel"] as? JSONDictionary else { - throw FeedParserError(.rssChannelNotFound) - } - - // I’d bet money that in practice the items array won’t always appear correctly inside the channel object. - // I’d also bet that sometimes it gets called "items" instead of "item". - var itemsObject = channelObject["item"] as? JSONArray - if itemsObject == nil { - itemsObject = parsedObject["item"] as? JSONArray - } - if itemsObject == nil { - itemsObject = channelObject["items"] as? JSONArray - } - if itemsObject == nil { - itemsObject = parsedObject["items"] as? JSONArray - } - if itemsObject == nil { - throw FeedParserError(.rssItemsNotFound) - } - - let title = channelObject["title"] as? String - let homePageURL = channelObject["link"] as? String - let feedURL = parserData.url - let feedDescription = channelObject["description"] as? String - - let items = parseItems(itemsObject!, parserData.url) - - return ParsedFeed(type: .rssInJSON, title: title, homePageURL: homePageURL, feedURL: feedURL, feedDescription: feedDescription, nextURL: nil, iconURL: nil, faviconURL: nil, authors: nil, expired: false, hubs: nil, items: items) - - } - catch { throw error } - } -} - -private extension RSSInJSONParser { - - static func parseItems(_ itemsObject: JSONArray, _ feedURL: String) -> Set { - - return Set(itemsObject.compactMap{ (oneItemDictionary) -> ParsedItem? in - - return parsedItemWithDictionary(oneItemDictionary, feedURL) - }) - } - - static func parsedItemWithDictionary(_ itemDictionary: JSONDictionary, _ feedURL: String) -> ParsedItem? { - - let externalURL = itemDictionary["link"] as? String - let title = itemDictionary["title"] as? String - - var contentHTML = itemDictionary["description"] as? String - var contentText: String? = nil - if contentHTML != nil && !(contentHTML!.contains("<")) { - contentText = contentHTML - contentHTML = nil - } - if contentHTML == nil && contentText == nil && title == nil { - return nil - } - - var datePublished: Date? = nil - if let datePublishedString = itemDictionary["pubDate"] as? String { - datePublished = RSDateWithString(datePublishedString) - } - - let authors = parseAuthors(itemDictionary) - let tags = parseTags(itemDictionary) - let attachments = parseAttachments(itemDictionary) - - var uniqueID: String? = itemDictionary["guid"] as? String - if uniqueID == nil { - - // Calculate a uniqueID based on a combination of non-empty elements. Then hash the result. - // Items should have guids. When they don't, re-runs are very likely - // because there's no other 100% reliable way to determine identity. - // This calculated uniqueID is valid only for this particular feed. (Just like ids in JSON Feed.) - - var s = "" - if let datePublished = datePublished { - s += "\(datePublished.timeIntervalSince1970)" - } - if let title = title { - s += title - } - if let externalURL = externalURL { - s += externalURL - } - if let authorEmailAddress = authors?.first?.emailAddress { - s += authorEmailAddress - } - if let oneAttachmentURL = attachments?.first?.url { - s += oneAttachmentURL - } - if s.isEmpty { - // Sheesh. Tough case. - if let _ = contentHTML { - s = contentHTML! - } - if let _ = contentText { - s = contentText! - } - } - uniqueID = (s as NSString).rsparser_md5Hash() - } - - if let uniqueID = uniqueID { - return ParsedItem(syncServiceID: nil, uniqueID: uniqueID, feedURL: feedURL, url: nil, externalURL: externalURL, title: title, contentHTML: contentHTML, contentText: contentText, summary: nil, imageURL: nil, bannerImageURL: nil, datePublished: datePublished, dateModified: nil, authors: authors, tags: tags, attachments: attachments) - } - return nil - } - - static func parseAuthors(_ itemDictionary: JSONDictionary) -> Set? { - - guard let authorEmailAddress = itemDictionary["author"] as? String else { - return nil - } - let parsedAuthor = ParsedAuthor(name: nil, url: nil, avatarURL: nil, emailAddress: authorEmailAddress) - return Set([parsedAuthor]) - } - - static func parseTags(_ itemDictionary: JSONDictionary) -> Set? { - - if let categoryObject = itemDictionary["category"] as? JSONDictionary { - if let oneTag = categoryObject["#value"] as? String { - return Set([oneTag]) - } - return nil - } - else if let categoryArray = itemDictionary["category"] as? JSONArray { - return Set(categoryArray.compactMap{ $0["#value"] as? String }) - } - return nil - } - - static func parseAttachments(_ itemDictionary: JSONDictionary) -> Set? { - - guard let enclosureObject = itemDictionary["enclosure"] as? JSONDictionary else { - return nil - } - guard let attachmentURL = enclosureObject["url"] as? String else { - return nil - } - - var attachmentSize = enclosureObject["length"] as? Int - if attachmentSize == nil { - if let attachmentSizeString = enclosureObject["length"] as? String { - attachmentSize = (attachmentSizeString as NSString).integerValue - } - } - - let type = enclosureObject["type"] as? String - if let attachment = ParsedAttachment(url: attachmentURL, mimeType: type, title: nil, sizeInBytes: attachmentSize, durationInSeconds: nil) { - return Set([attachment]) - } - return nil - } -} diff --git a/Frameworks/RSParser/Feeds/ParsedAttachment.swift b/Frameworks/RSParser/Feeds/ParsedAttachment.swift deleted file mode 100644 index 7dd008d24..000000000 --- a/Frameworks/RSParser/Feeds/ParsedAttachment.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// ParsedAttachment.swift -// RSParser -// -// Created by Brent Simmons on 6/20/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -public struct ParsedAttachment: Hashable { - - public let url: String - public let mimeType: String? - public let title: String? - public let sizeInBytes: Int? - public let durationInSeconds: Int? - public let hashValue: Int - - init?(url: String, mimeType: String?, title: String?, sizeInBytes: Int?, durationInSeconds: Int?) { - - if url.isEmpty { - return nil - } - - self.url = url - self.mimeType = mimeType - self.title = title - self.sizeInBytes = sizeInBytes - self.durationInSeconds = durationInSeconds - self.hashValue = url.hashValue - } - - public static func ==(lhs: ParsedAttachment, rhs: ParsedAttachment) -> Bool { - - return lhs.hashValue == rhs.hashValue && lhs.url == rhs.url && lhs.mimeType == rhs.mimeType && lhs.title == rhs.title && lhs.sizeInBytes == rhs.sizeInBytes && lhs.durationInSeconds == rhs.durationInSeconds - } -} diff --git a/Frameworks/RSParser/Feeds/ParsedAuthor.swift b/Frameworks/RSParser/Feeds/ParsedAuthor.swift deleted file mode 100644 index cc8013ace..000000000 --- a/Frameworks/RSParser/Feeds/ParsedAuthor.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// ParsedAuthor.swift -// RSParser -// -// Created by Brent Simmons on 6/20/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -public struct ParsedAuthor: Hashable { - - public let name: String? - public let url: String? - public let avatarURL: String? - public let emailAddress: String? - public let hashValue: Int - - init(name: String?, url: String?, avatarURL: String?, emailAddress: String?) { - - self.name = name - self.url = url - self.avatarURL = avatarURL - self.emailAddress = emailAddress - - var stringToHash = "" - stringToHash += name ?? "" - stringToHash += url ?? "" - stringToHash += avatarURL ?? "" - stringToHash += emailAddress ?? "" - self.hashValue = stringToHash.hashValue - } - - public static func ==(lhs: ParsedAuthor, rhs: ParsedAuthor) -> Bool { - - return lhs.hashValue == rhs.hashValue && lhs.name == rhs.name && lhs.url == rhs.url && lhs.avatarURL == rhs.avatarURL && lhs.emailAddress == rhs.emailAddress - } -} diff --git a/Frameworks/RSParser/Feeds/ParsedFeed.swift b/Frameworks/RSParser/Feeds/ParsedFeed.swift deleted file mode 100644 index b4c2253e6..000000000 --- a/Frameworks/RSParser/Feeds/ParsedFeed.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// ParsedFeed.swift -// RSParser -// -// Created by Brent Simmons on 6/20/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -public struct ParsedFeed { - - public let type: FeedType - public let title: String? - public let homePageURL: String? - public let feedURL: String? - public let feedDescription: String? - public let nextURL: String? - public let iconURL: String? - public let faviconURL: String? - public let authors: Set? - public let expired: Bool - public let hubs: Set? - public let items: Set - - init(type: FeedType, title: String?, homePageURL: String?, feedURL: String?, feedDescription: String?, nextURL: String?, iconURL: String?, faviconURL: String?, authors: Set?, expired: Bool, hubs: Set?, items: Set) { - - self.type = type - self.title = title - self.homePageURL = homePageURL - self.feedURL = feedURL - self.feedDescription = feedDescription - self.nextURL = nextURL - self.iconURL = iconURL - self.faviconURL = faviconURL - self.authors = authors - self.expired = expired - self.hubs = hubs - self.items = items - } -} - diff --git a/Frameworks/RSParser/Feeds/ParsedHub.swift b/Frameworks/RSParser/Feeds/ParsedHub.swift deleted file mode 100644 index 585cc5654..000000000 --- a/Frameworks/RSParser/Feeds/ParsedHub.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// ParsedHub.swift -// RSParser -// -// Created by Brent Simmons on 6/20/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -public struct ParsedHub: Hashable { - - public let type: String - public let url: String - public let hashValue: Int - - init(type: String, url: String) { - - self.type = type - self.url = url - self.hashValue = url.hashValue - } - - public static func ==(lhs: ParsedHub, rhs: ParsedHub) -> Bool { - - return lhs.type == rhs.type && lhs.url == rhs.url - } -} diff --git a/Frameworks/RSParser/Feeds/ParsedItem.swift b/Frameworks/RSParser/Feeds/ParsedItem.swift deleted file mode 100644 index 216dd5e24..000000000 --- a/Frameworks/RSParser/Feeds/ParsedItem.swift +++ /dev/null @@ -1,58 +0,0 @@ -// -// ParsedItem.swift -// RSParser -// -// Created by Brent Simmons on 6/20/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -public struct ParsedItem: Hashable { - - public let syncServiceID: String? //Nil when not syncing - public let uniqueID: String //RSS guid, for instance; may be calculated - public let feedURL: String - public let url: String? - public let externalURL: String? - public let title: String? - public let contentHTML: String? - public let contentText: String? - public let summary: String? - public let imageURL: String? - public let bannerImageURL: String? - public let datePublished: Date? - public let dateModified: Date? - public let authors: Set? - public let tags: Set? - public let attachments: Set? - public let hashValue: Int - - init(syncServiceID: String?, uniqueID: String, feedURL: String, url: String?, externalURL: String?, title: String?, contentHTML: String?, contentText: String?, summary: String?, imageURL: String?, bannerImageURL: String?, datePublished: Date?, dateModified: Date?, authors: Set?, tags: Set?, attachments: Set?) { - - self.syncServiceID = syncServiceID - self.uniqueID = uniqueID - self.feedURL = feedURL - self.url = url - self.externalURL = externalURL - self.title = title - self.contentHTML = contentHTML - self.contentText = contentText - self.summary = summary - self.imageURL = imageURL - self.bannerImageURL = bannerImageURL - self.datePublished = datePublished - self.dateModified = dateModified - self.authors = authors - self.tags = tags - self.attachments = attachments - self.hashValue = (feedURL + uniqueID).hashValue - } - - public static func ==(lhs: ParsedItem, rhs: ParsedItem) -> Bool { - - // TODO: compare all properties. - return lhs.hashValue == rhs.hashValue && lhs.uniqueID == rhs.uniqueID && lhs.feedURL == rhs.feedURL - } -} - diff --git a/Frameworks/RSParser/Feeds/XML/AtomParser.swift b/Frameworks/RSParser/Feeds/XML/AtomParser.swift deleted file mode 100644 index 151349af7..000000000 --- a/Frameworks/RSParser/Feeds/XML/AtomParser.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// AtomParser.swift -// RSParser -// -// Created by Brent Simmons on 6/25/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -// RSSParser wraps the Objective-C RSAtomParser. -// -// The Objective-C parser creates RSParsedFeed, RSParsedArticle, etc. -// This wrapper then creates ParsedFeed, ParsedItem, etc. so that it creates -// the same things that JSONFeedParser and RSSInJSONParser create. -// -// In general, you should see FeedParser.swift for all your feed-parsing needs. - -public struct AtomParser { - - public static func parse(_ parserData: ParserData) -> ParsedFeed? { - - if let rsParsedFeed = RSAtomParser.parseFeed(with: parserData) { - return RSParsedFeedTransformer.parsedFeed(rsParsedFeed) - } - return nil - } -} diff --git a/Frameworks/RSParser/Feeds/XML/RSAtomParser.h b/Frameworks/RSParser/Feeds/XML/RSAtomParser.h deleted file mode 100755 index 27b5d80e4..000000000 --- a/Frameworks/RSParser/Feeds/XML/RSAtomParser.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// RSAtomParser.h -// RSParser -// -// Created by Brent Simmons on 1/15/15. -// Copyright (c) 2015 Ranchero Software LLC. All rights reserved. -// - -@import Foundation; - -@class ParserData; -@class RSParsedFeed; - -@interface RSAtomParser : NSObject - -+ (RSParsedFeed *)parseFeedWithData:(ParserData *)parserData; - -@end diff --git a/Frameworks/RSParser/Feeds/XML/RSAtomParser.m b/Frameworks/RSParser/Feeds/XML/RSAtomParser.m deleted file mode 100755 index fa326636d..000000000 --- a/Frameworks/RSParser/Feeds/XML/RSAtomParser.m +++ /dev/null @@ -1,635 +0,0 @@ -// -// RSAtomParser.m -// RSParser -// -// Created by Brent Simmons on 1/15/15. -// Copyright (c) 2015 Ranchero Software LLC. All rights reserved. -// - -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import - -@interface RSAtomParser () - -@property (nonatomic) NSData *feedData; -@property (nonatomic) NSString *urlString; -@property (nonatomic) BOOL endFeedFound; -@property (nonatomic) BOOL parsingXHTML; -@property (nonatomic) BOOL parsingSource; -@property (nonatomic) BOOL parsingArticle; -@property (nonatomic) BOOL parsingAuthor; -@property (nonatomic) NSMutableArray *attributesStack; -@property (nonatomic, readonly) NSDictionary *currentAttributes; -@property (nonatomic) NSMutableString *xhtmlString; -@property (nonatomic) NSString *link; -@property (nonatomic) NSString *title; -@property (nonatomic) NSMutableArray *articles; -@property (nonatomic) NSDate *dateParsed; -@property (nonatomic) RSSAXParser *parser; -@property (nonatomic, readonly) RSParsedArticle *currentArticle; -@property (nonatomic) RSParsedAuthor *currentAuthor; -@property (nonatomic, readonly) NSDate *currentDate; - -@end - - -@implementation RSAtomParser - -#pragma mark - Class Methods - -+ (RSParsedFeed *)parseFeedWithData:(ParserData *)parserData { - - RSAtomParser *parser = [[[self class] alloc] initWithParserData:parserData]; - return [parser parseFeed]; -} - - -#pragma mark - Init - -- (instancetype)initWithParserData:(ParserData *)parserData { - - self = [super init]; - if (!self) { - return nil; - } - - _feedData = parserData.data; - _urlString = parserData.url; - _parser = [[RSSAXParser alloc] initWithDelegate:self]; - _attributesStack = [NSMutableArray new]; - _articles = [NSMutableArray new]; - - return self; -} - - -#pragma mark - API - -- (RSParsedFeed *)parseFeed { - - [self parse]; - - RSParsedFeed *parsedFeed = [[RSParsedFeed alloc] initWithURLString:self.urlString title:self.title link:self.link articles:self.articles]; - - return parsedFeed; -} - - -#pragma mark - Constants - -static NSString *kTypeKey = @"type"; -static NSString *kXHTMLType = @"xhtml"; -static NSString *kRelKey = @"rel"; -static NSString *kAlternateValue = @"alternate"; -static NSString *kHrefKey = @"href"; -static NSString *kXMLKey = @"xml"; -static NSString *kBaseKey = @"base"; -static NSString *kLangKey = @"lang"; -static NSString *kXMLBaseKey = @"xml:base"; -static NSString *kXMLLangKey = @"xml:lang"; -static NSString *kTextHTMLValue = @"text/html"; -static NSString *kRelatedValue = @"related"; -static NSString *kEnclosureValue = @"enclosure"; -static NSString *kShortURLValue = @"shorturl"; -static NSString *kHTMLValue = @"html"; -static NSString *kEnValue = @"en"; -static NSString *kTextValue = @"text"; -static NSString *kSelfValue = @"self"; -static NSString *kLengthKey = @"length"; -static NSString *kTitleKey = @"title"; - -static const char *kID = "id"; -static const NSInteger kIDLength = 3; - -static const char *kTitle = "title"; -static const NSInteger kTitleLength = 6; - -static const char *kContent = "content"; -static const NSInteger kContentLength = 8; - -static const char *kSummary = "summary"; -static const NSInteger kSummaryLength = 8; - -static const char *kLink = "link"; -static const NSInteger kLinkLength = 5; - -static const char *kPublished = "published"; -static const NSInteger kPublishedLength = 10; - -static const char *kUpdated = "updated"; -static const NSInteger kUpdatedLength = 8; - -static const char *kAuthor = "author"; -static const NSInteger kAuthorLength = 7; - -static const char *kName = "name"; -static const NSInteger kNameLength = 5; - -static const char *kEmail = "email"; -static const NSInteger kEmailLength = 6; - -static const char *kURI = "uri"; -static const NSInteger kURILength = 4; - -static const char *kEntry = "entry"; -static const NSInteger kEntryLength = 6; - -static const char *kSource = "source"; -static const NSInteger kSourceLength = 7; - -static const char *kFeed = "feed"; -static const NSInteger kFeedLength = 5; - -static const char *kType = "type"; -static const NSInteger kTypeLength = 5; - -static const char *kRel = "rel"; -static const NSInteger kRelLength = 4; - -static const char *kAlternate = "alternate"; -static const NSInteger kAlternateLength = 10; - -static const char *kHref = "href"; -static const NSInteger kHrefLength = 5; - -static const char *kXML = "xml"; -static const NSInteger kXMLLength = 4; - -static const char *kBase = "base"; -static const NSInteger kBaseLength = 5; - -static const char *kLang = "lang"; -static const NSInteger kLangLength = 5; - -static const char *kTextHTML = "text/html"; -static const NSInteger kTextHTMLLength = 10; - -static const char *kRelated = "related"; -static const NSInteger kRelatedLength = 8; - -static const char *kShortURL = "shorturl"; -static const NSInteger kShortURLLength = 9; - -static const char *kHTML = "html"; -static const NSInteger kHTMLLength = 5; - -static const char *kEn = "en"; -static const NSInteger kEnLength = 3; - -static const char *kText = "text"; -static const NSInteger kTextLength = 5; - -static const char *kSelf = "self"; -static const NSInteger kSelfLength = 5; - -static const char *kEnclosure = "enclosure"; -static const NSInteger kEnclosureLength = 10; - -static const char *kLength = "length"; -static const NSInteger kLengthLength = 7; - -#pragma mark - Parsing - -- (void)parse { - - self.dateParsed = [NSDate date]; - - @autoreleasepool { - [self.parser parseData:self.feedData]; - [self.parser finishParsing]; - } -} - - -- (void)addArticle { - - RSParsedArticle *article = [[RSParsedArticle alloc] initWithFeedURL:self.urlString]; - article.dateParsed = self.dateParsed; - - [self.articles addObject:article]; -} - - -- (RSParsedArticle *)currentArticle { - - return self.articles.lastObject; -} - - -- (NSDictionary *)currentAttributes { - - return self.attributesStack.lastObject; -} - - -- (NSDate *)currentDate { - - return RSDateWithBytes(self.parser.currentCharacters.bytes, self.parser.currentCharacters.length); -} - - -- (void)addFeedLink { - - if (self.link && self.link.length > 0) { - return; - } - - NSString *related = self.currentAttributes[kRelKey]; - if (related == kAlternateValue) { - self.link = self.currentAttributes[kHrefKey]; - } -} - - -- (void)addFeedTitle { - - if (self.title.length < 1) { - self.title = self.parser.currentStringWithTrimmedWhitespace; - } -} - -- (void)addLink { - - NSDictionary *attributes = self.currentAttributes; - - NSString *urlString = attributes[kHrefKey]; - if (urlString.length < 1) { - return; - } - - RSParsedArticle *article = self.currentArticle; - - NSString *rel = attributes[kRelKey]; - if (rel.length < 1) { - rel = kAlternateValue; - } - - if (rel == kAlternateValue) { - if (!article.link) { - article.link = urlString; - } - } - else if (rel == kRelatedValue) { - if (!article.permalink) { - article.permalink = urlString; - } - } - else if (rel == kEnclosureValue) { - RSParsedEnclosure *enclosure = [self enclosureWithURLString:urlString attributes:attributes]; - [article addEnclosure:enclosure]; - } -} - -- (RSParsedEnclosure *)enclosureWithURLString:(NSString *)urlString attributes:(NSDictionary *)attributes { - - RSParsedEnclosure *enclosure = [[RSParsedEnclosure alloc] init]; - enclosure.url = urlString; - enclosure.title = attributes[kTitleKey]; - enclosure.mimeType = attributes[kTypeKey]; - enclosure.length = [attributes[kLengthKey] integerValue]; - - return enclosure; -} - -- (void)addContent { - - self.currentArticle.body = [self currentStringWithHTMLEntitiesDecoded]; -} - - -- (void)addSummary { - - if (!self.currentArticle.body) { - self.currentArticle.body = [self currentStringWithHTMLEntitiesDecoded]; - } -} - - -- (NSString *)currentStringWithHTMLEntitiesDecoded { - - return [self.parser.currentStringWithTrimmedWhitespace rsparser_stringByDecodingHTMLEntities]; -} - - -- (void)addArticleElement:(const xmlChar *)localName prefix:(const xmlChar *)prefix { - - if (prefix) { - return; - } - - if (RSSAXEqualTags(localName, kID, kIDLength)) { - self.currentArticle.guid = self.parser.currentStringWithTrimmedWhitespace; - } - - else if (RSSAXEqualTags(localName, kTitle, kTitleLength)) { - self.currentArticle.title = [self currentStringWithHTMLEntitiesDecoded]; - } - - else if (RSSAXEqualTags(localName, kContent, kContentLength)) { - [self addContent]; - } - - else if (RSSAXEqualTags(localName, kSummary, kSummaryLength)) { - [self addSummary]; - } - - else if (RSSAXEqualTags(localName, kLink, kLinkLength)) { - [self addLink]; - } - - else if (RSSAXEqualTags(localName, kPublished, kPublishedLength)) { - self.currentArticle.datePublished = self.currentDate; - } - - else if (RSSAXEqualTags(localName, kUpdated, kUpdatedLength)) { - self.currentArticle.dateModified = self.currentDate; - } -} - - -- (void)addXHTMLTag:(const xmlChar *)localName { - - if (!localName) { - return; - } - - [self.xhtmlString appendString:@"<"]; - [self.xhtmlString appendString:[NSString stringWithUTF8String:(const char *)localName]]; - - if (self.currentAttributes.count < 1) { - [self.xhtmlString appendString:@">"]; - return; - } - - for (NSString *oneKey in self.currentAttributes) { - - [self.xhtmlString appendString:@" "]; - - NSString *oneValue = self.currentAttributes[oneKey]; - [self.xhtmlString appendString:oneKey]; - - [self.xhtmlString appendString:@"=\""]; - - oneValue = [oneValue stringByReplacingOccurrencesOfString:@"\"" withString:@"""]; - [self.xhtmlString appendString:oneValue]; - - [self.xhtmlString appendString:@"\""]; - } - - [self.xhtmlString appendString:@">"]; -} - - -#pragma mark - RSSAXParserDelegate - -- (void)saxParser:(RSSAXParser *)SAXParser XMLStartElement:(const xmlChar *)localName prefix:(const xmlChar *)prefix uri:(const xmlChar *)uri numberOfNamespaces:(NSInteger)numberOfNamespaces namespaces:(const xmlChar **)namespaces numberOfAttributes:(NSInteger)numberOfAttributes numberDefaulted:(int)numberDefaulted attributes:(const xmlChar **)attributes { - - if (self.endFeedFound) { - return; - } - - NSDictionary *xmlAttributes = [self.parser attributesDictionary:attributes numberOfAttributes:numberOfAttributes]; - if (!xmlAttributes) { - xmlAttributes = [NSDictionary dictionary]; - } - [self.attributesStack addObject:xmlAttributes]; - - if (self.parsingXHTML) { - [self addXHTMLTag:localName]; - return; - } - - if (RSSAXEqualTags(localName, kEntry, kEntryLength)) { - self.parsingArticle = YES; - [self addArticle]; - return; - } - - if (RSSAXEqualTags(localName, kAuthor, kAuthorLength)) { - self.parsingAuthor = YES; - self.currentAuthor = [[RSParsedAuthor alloc] init]; - return; - } - - if (RSSAXEqualTags(localName, kSource, kSourceLength)) { - self.parsingSource = YES; - return; - } - - BOOL isContentTag = RSSAXEqualTags(localName, kContent, kContentLength); - BOOL isSummaryTag = RSSAXEqualTags(localName, kSummary, kSummaryLength); - if (self.parsingArticle && (isContentTag || isSummaryTag)) { - - NSString *contentType = xmlAttributes[kTypeKey]; - if ([contentType isEqualToString:kXHTMLType]) { - self.parsingXHTML = YES; - self.xhtmlString = [NSMutableString stringWithString:@""]; - return; - } - } - - if (!self.parsingArticle && RSSAXEqualTags(localName, kLink, kLinkLength)) { - [self addFeedLink]; - return; - } - - [self.parser beginStoringCharacters]; -} - - -- (void)saxParser:(RSSAXParser *)SAXParser XMLEndElement:(const xmlChar *)localName prefix:(const xmlChar *)prefix uri:(const xmlChar *)uri { - - if (RSSAXEqualTags(localName, kFeed, kFeedLength)) { - self.endFeedFound = YES; - return; - } - - if (self.endFeedFound) { - return; - } - - if (self.parsingXHTML) { - - BOOL isContentTag = RSSAXEqualTags(localName, kContent, kContentLength); - BOOL isSummaryTag = RSSAXEqualTags(localName, kSummary, kSummaryLength); - - if (self.parsingArticle && (isContentTag || isSummaryTag)) { - - if (isContentTag) { - self.currentArticle.body = [self.xhtmlString copy]; - } - - else if (isSummaryTag) { - if (self.currentArticle.body.length < 1) { - self.currentArticle.body = [self.xhtmlString copy]; - } - } - } - - if (isContentTag || isSummaryTag) { - self.parsingXHTML = NO; - } - - [self.xhtmlString appendString:@""]; - } - - else if (self.parsingAuthor) { - - if (RSSAXEqualTags(localName, kAuthor, kAuthorLength)) { - self.parsingAuthor = NO; - RSParsedAuthor *author = self.currentAuthor; - if (author.name || author.emailAddress || author.url) { - [self.currentArticle addAuthor:author]; - } - self.currentAuthor = nil; - } - else if (RSSAXEqualTags(localName, kName, kNameLength)) { - self.currentAuthor.name = self.parser.currentStringWithTrimmedWhitespace; - } - else if (RSSAXEqualTags(localName, kEmail, kEmailLength)) { - self.currentAuthor.emailAddress = self.parser.currentStringWithTrimmedWhitespace; - } - else if (RSSAXEqualTags(localName, kURI, kURILength)) { - self.currentAuthor.url = self.parser.currentStringWithTrimmedWhitespace; - } - } - - else if (RSSAXEqualTags(localName, kEntry, kEntryLength)) { - self.parsingArticle = NO; - } - - else if (self.parsingArticle && !self.parsingSource) { - [self addArticleElement:localName prefix:prefix]; - } - - else if (RSSAXEqualTags(localName, kSource, kSourceLength)) { - self.parsingSource = NO; - } - - else if (!self.parsingArticle && !self.parsingSource && RSSAXEqualTags(localName, kTitle, kTitleLength)) { - [self addFeedTitle]; - } - [self.attributesStack removeLastObject]; -} - - -- (NSString *)saxParser:(RSSAXParser *)SAXParser internedStringForName:(const xmlChar *)name prefix:(const xmlChar *)prefix { - - if (prefix && RSSAXEqualTags(prefix, kXML, kXMLLength)) { - - if (RSSAXEqualTags(name, kBase, kBaseLength)) { - return kXMLBaseKey; - } - if (RSSAXEqualTags(name, kLang, kLangLength)) { - return kXMLLangKey; - } - } - - if (prefix) { - return nil; - } - - if (RSSAXEqualTags(name, kRel, kRelLength)) { - return kRelKey; - } - - if (RSSAXEqualTags(name, kType, kTypeLength)) { - return kTypeKey; - } - - if (RSSAXEqualTags(name, kHref, kHrefLength)) { - return kHrefKey; - } - - if (RSSAXEqualTags(name, kAlternate, kAlternateLength)) { - return kAlternateValue; - } - - if (RSSAXEqualTags(name, kLength, kLengthLength)) { - return kLengthKey; - } - - if (RSSAXEqualTags(name, kTitle, kTitleLength)) { - return kTitleKey; - } - - return nil; -} - - -static BOOL equalBytes(const void *bytes1, const void *bytes2, NSUInteger length) { - - return memcmp(bytes1, bytes2, length) == 0; -} - - -- (NSString *)saxParser:(RSSAXParser *)SAXParser internedStringForValue:(const void *)bytes length:(NSUInteger)length { - - static const NSUInteger alternateLength = kAlternateLength - 1; - static const NSUInteger textHTMLLength = kTextHTMLLength - 1; - static const NSUInteger relatedLength = kRelatedLength - 1; - static const NSUInteger shortURLLength = kShortURLLength - 1; - static const NSUInteger htmlLength = kHTMLLength - 1; - static const NSUInteger enLength = kEnLength - 1; - static const NSUInteger textLength = kTextLength - 1; - static const NSUInteger selfLength = kSelfLength - 1; - static const NSUInteger enclosureLength = kEnclosureLength - 1; - - if (length == alternateLength && equalBytes(bytes, kAlternate, alternateLength)) { - return kAlternateValue; - } - - if (length == enclosureLength && equalBytes(bytes, kEnclosure, enclosureLength)) { - return kEnclosureValue; - } - - if (length == textHTMLLength && equalBytes(bytes, kTextHTML, textHTMLLength)) { - return kTextHTMLValue; - } - - if (length == relatedLength && equalBytes(bytes, kRelated, relatedLength)) { - return kRelatedValue; - } - - if (length == shortURLLength && equalBytes(bytes, kShortURL, shortURLLength)) { - return kShortURLValue; - } - - if (length == htmlLength && equalBytes(bytes, kHTML, htmlLength)) { - return kHTMLValue; - } - - if (length == enLength && equalBytes(bytes, kEn, enLength)) { - return kEnValue; - } - - if (length == textLength && equalBytes(bytes, kText, textLength)) { - return kTextValue; - } - - if (length == selfLength && equalBytes(bytes, kSelf, selfLength)) { - return kSelfValue; - } - - return nil; -} - - -- (void)saxParser:(RSSAXParser *)SAXParser XMLCharactersFound:(const unsigned char *)characters length:(NSUInteger)length { - - if (self.parsingXHTML) { - [self.xhtmlString appendString:[[NSString alloc] initWithBytesNoCopy:(void *)characters length:length encoding:NSUTF8StringEncoding freeWhenDone:NO]]; - } -} - -@end diff --git a/Frameworks/RSParser/Feeds/XML/RSParsedArticle.h b/Frameworks/RSParser/Feeds/XML/RSParsedArticle.h deleted file mode 100755 index d5ae83b7b..000000000 --- a/Frameworks/RSParser/Feeds/XML/RSParsedArticle.h +++ /dev/null @@ -1,36 +0,0 @@ -// -// RSParsedArticle.h -// RSParser -// -// Created by Brent Simmons on 12/6/14. -// Copyright (c) 2014 Ranchero Software LLC. All rights reserved. -// - -@import Foundation; - -@class RSParsedEnclosure; -@class RSParsedAuthor; - -@interface RSParsedArticle : NSObject - -- (nonnull instancetype)initWithFeedURL:(NSString * _Nonnull)feedURL; - -@property (nonatomic, readonly, nonnull) NSString *feedURL; -@property (nonatomic, nonnull) NSString *articleID; //guid, if present, or calculated from other attributes. Should be unique to the feed, but not necessarily unique across different feeds. (Not suitable for a database ID.) - -@property (nonatomic, nullable) NSString *guid; -@property (nonatomic, nullable) NSString *title; -@property (nonatomic, nullable) NSString *body; -@property (nonatomic, nullable) NSString *link; -@property (nonatomic, nullable) NSString *permalink; -@property (nonatomic, nullable) NSSet *authors; -@property (nonatomic, nullable) NSSet *enclosures; -@property (nonatomic, nullable) NSDate *datePublished; -@property (nonatomic, nullable) NSDate *dateModified; -@property (nonatomic, nonnull) NSDate *dateParsed; - -- (void)addEnclosure:(RSParsedEnclosure *_Nonnull)enclosure; -- (void)addAuthor:(RSParsedAuthor *_Nonnull)author; - -@end - diff --git a/Frameworks/RSParser/Feeds/XML/RSParsedArticle.m b/Frameworks/RSParser/Feeds/XML/RSParsedArticle.m deleted file mode 100755 index 436f5afc3..000000000 --- a/Frameworks/RSParser/Feeds/XML/RSParsedArticle.m +++ /dev/null @@ -1,131 +0,0 @@ -// -// RSParsedArticle.m -// RSParser -// -// Created by Brent Simmons on 12/6/14. -// Copyright (c) 2014 Ranchero Software LLC. All rights reserved. -// - -#import -#import -#import -#import -#import - -@implementation RSParsedArticle - - -#pragma mark - Init - -- (instancetype)initWithFeedURL:(NSString *)feedURL { - - NSParameterAssert(feedURL != nil); - - self = [super init]; - if (!self) { - return nil; - } - - _feedURL = feedURL; - _dateParsed = [NSDate date]; - - return self; -} - - -#pragma mark - Enclosures - -- (void)addEnclosure:(RSParsedEnclosure *)enclosure { - - if (self.enclosures) { - self.enclosures = [self.enclosures setByAddingObject:enclosure]; - } - else { - self.enclosures = [NSSet setWithObject:enclosure]; - } -} - -#pragma mark - Authors - -- (void)addAuthor:(RSParsedAuthor *)author { - - if (self.authors) { - self.authors = [self.authors setByAddingObject:author]; - } - else { - self.authors = [NSSet setWithObject:author]; - } -} - -#pragma mark - articleID - -- (NSString *)articleID { - - if (self.guid) { - return self.guid; - } - - if (!_articleID) { - _articleID = [self calculatedArticleID]; - } - - return _articleID; -} - - -- (NSString *)calculatedArticleID { - - /*Concatenate a combination of properties when no guid. Then hash the result. - In general, feeds should have guids. When they don't, re-runs are very likely, - because there's no other 100% reliable way to determine identity. - This is intended to create an ID unique inside a feed, but not globally unique. - Not suitable for a database ID, in other words.*/ - - NSMutableString *s = [NSMutableString stringWithString:@""]; - - NSString *datePublishedTimeStampString = nil; - if (self.datePublished) { - datePublishedTimeStampString = [NSString stringWithFormat:@"%.0f", self.datePublished.timeIntervalSince1970]; - } - - // Ideally we have a permalink and a pubDate. Either one would probably be a good guid, but together they should be rock-solid. (In theory. Feeds are buggy, though.) - if (!RSParserStringIsEmpty(self.permalink) && datePublishedTimeStampString) { - [s appendString:self.permalink]; - [s appendString:datePublishedTimeStampString]; - } - - else if (!RSParserStringIsEmpty(self.link) && datePublishedTimeStampString) { - [s appendString:self.link]; - [s appendString:datePublishedTimeStampString]; - } - - else if (!RSParserStringIsEmpty(self.title) && datePublishedTimeStampString) { - [s appendString:self.title]; - [s appendString:datePublishedTimeStampString]; - } - - else if (datePublishedTimeStampString) { - [s appendString:datePublishedTimeStampString]; - } - - else if (!RSParserStringIsEmpty(self.permalink)) { - [s appendString:self.permalink]; - } - - else if (!RSParserStringIsEmpty(self.link)) { - [s appendString:self.link]; - } - - else if (!RSParserStringIsEmpty(self.title)) { - [s appendString:self.title]; - } - - else if (!RSParserStringIsEmpty(self.body)) { - [s appendString:self.body]; - } - - return [s rsparser_md5Hash]; -} - -@end - diff --git a/Frameworks/RSParser/Feeds/XML/RSParsedAuthor.h b/Frameworks/RSParser/Feeds/XML/RSParsedAuthor.h deleted file mode 100644 index 2c28236a2..000000000 --- a/Frameworks/RSParser/Feeds/XML/RSParsedAuthor.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// RSParsedAuthor.h -// RSParserTests -// -// Created by Brent Simmons on 12/19/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -@import Foundation; - -@interface RSParsedAuthor : NSObject - -@property (nonatomic, nullable) NSString *name; -@property (nonatomic, nullable) NSString *emailAddress; -@property (nonatomic, nullable) NSString *url; - -+ (instancetype _Nonnull )authorWithSingleString:(NSString *_Nonnull)s; // Don’t know which property it is. Guess based on contents of the string. Common with RSS. - -@end diff --git a/Frameworks/RSParser/Feeds/XML/RSParsedAuthor.m b/Frameworks/RSParser/Feeds/XML/RSParsedAuthor.m deleted file mode 100644 index 01faf6306..000000000 --- a/Frameworks/RSParser/Feeds/XML/RSParsedAuthor.m +++ /dev/null @@ -1,33 +0,0 @@ -// -// RSParsedAuthor.m -// RSParserTests -// -// Created by Brent Simmons on 12/19/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -#import -#import "RSParsedAuthor.h" - -@implementation RSParsedAuthor - -+ (instancetype)authorWithSingleString:(NSString *)s { - - // The author element in RSS is supposed to be email address — but often it’s a name, and sometimes a URL. - - RSParsedAuthor *author = [[self alloc] init]; - - if ([s rsparser_contains:@"@"]) { - author.emailAddress = s; - } - else if ([s.lowercaseString hasPrefix:@"http"]) { - author.url = s; - } - else { - author.name = s; - } - - return author; -} - -@end diff --git a/Frameworks/RSParser/Feeds/XML/RSParsedEnclosure.h b/Frameworks/RSParser/Feeds/XML/RSParsedEnclosure.h deleted file mode 100644 index 8fc9e404d..000000000 --- a/Frameworks/RSParser/Feeds/XML/RSParsedEnclosure.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// RSParsedEnclosure.h -// RSParser -// -// Created by Brent Simmons on 12/18/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -@import Foundation; - -NS_ASSUME_NONNULL_BEGIN - -@interface RSParsedEnclosure : NSObject - -@property (nonatomic) NSString *url; -@property (nonatomic) NSInteger length; -@property (nonatomic, nullable) NSString *mimeType; -@property (nonatomic, nullable) NSString *title; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Frameworks/RSParser/Feeds/XML/RSParsedEnclosure.m b/Frameworks/RSParser/Feeds/XML/RSParsedEnclosure.m deleted file mode 100644 index f6f35da59..000000000 --- a/Frameworks/RSParser/Feeds/XML/RSParsedEnclosure.m +++ /dev/null @@ -1,13 +0,0 @@ -// -// RSParsedEnclosure.m -// RSParser -// -// Created by Brent Simmons on 12/18/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -#import "RSParsedEnclosure.h" - -@implementation RSParsedEnclosure - -@end diff --git a/Frameworks/RSParser/Feeds/XML/RSParsedFeed.h b/Frameworks/RSParser/Feeds/XML/RSParsedFeed.h deleted file mode 100755 index dff915d78..000000000 --- a/Frameworks/RSParser/Feeds/XML/RSParsedFeed.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// RSParsedFeed.h -// RSParser -// -// Created by Brent Simmons on 7/12/15. -// Copyright © 2015 Ranchero Software, LLC. All rights reserved. -// - -@import Foundation; - -@class RSParsedArticle; - -@interface RSParsedFeed : NSObject - -- (nonnull instancetype)initWithURLString:(NSString * _Nonnull)urlString title:(NSString * _Nullable)title link:(NSString * _Nullable)link articles:(NSArray * _Nonnull)articles; - -@property (nonatomic, readonly, nonnull) NSString *urlString; -@property (nonatomic, readonly, nullable) NSString *title; -@property (nonatomic, readonly, nullable) NSString *link; -@property (nonatomic, readonly, nonnull) NSSet *articles; - -@end diff --git a/Frameworks/RSParser/Feeds/XML/RSParsedFeed.m b/Frameworks/RSParser/Feeds/XML/RSParsedFeed.m deleted file mode 100755 index 8b71b0b52..000000000 --- a/Frameworks/RSParser/Feeds/XML/RSParsedFeed.m +++ /dev/null @@ -1,29 +0,0 @@ -// -// RSParsedFeed.m -// RSParser -// -// Created by Brent Simmons on 7/12/15. -// Copyright © 2015 Ranchero Software, LLC. All rights reserved. -// - -#import - -@implementation RSParsedFeed - -- (instancetype)initWithURLString:(NSString *)urlString title:(NSString *)title link:(NSString *)link articles:(NSSet *)articles { - - self = [super init]; - if (!self) { - return nil; - } - - _urlString = urlString; - _title = title; - _link = link; - _articles = articles; - - return self; -} - - -@end diff --git a/Frameworks/RSParser/Feeds/XML/RSParsedFeedTransformer.swift b/Frameworks/RSParser/Feeds/XML/RSParsedFeedTransformer.swift deleted file mode 100644 index 2a4cbc458..000000000 --- a/Frameworks/RSParser/Feeds/XML/RSParsedFeedTransformer.swift +++ /dev/null @@ -1,76 +0,0 @@ -// -// RSParsedFeedTransformer.swift -// RSParser -// -// Created by Brent Simmons on 6/25/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -// RSRSSParser and RSAtomParser were written in Objective-C quite a while ago. -// They create an RSParsedFeed object and related Objective-C objects. -// These functions take an RSParsedFeed and return a Swift-y ParsedFeed, -// which is part of providing a single API for feed parsing. - -struct RSParsedFeedTransformer { - - static func parsedFeed(_ rsParsedFeed: RSParsedFeed) -> ParsedFeed { - - let items = parsedItems(rsParsedFeed.articles) - return ParsedFeed(type: .rss, title: rsParsedFeed.title, homePageURL: rsParsedFeed.link, feedURL: rsParsedFeed.urlString, feedDescription: nil, nextURL: nil, iconURL: nil, faviconURL: nil, authors: nil, expired: false, hubs: nil, items: items) - } -} - -private extension RSParsedFeedTransformer { - - static func parsedItems(_ parsedArticles: Set) -> Set { - - // Create Set from Set - - return Set(parsedArticles.map(parsedItem)) - } - - static func parsedItem(_ parsedArticle: RSParsedArticle) -> ParsedItem { - - let uniqueID = parsedArticle.articleID - let url = parsedArticle.permalink - let externalURL = parsedArticle.link - let title = parsedArticle.title - let contentHTML = parsedArticle.body - let datePublished = parsedArticle.datePublished - let dateModified = parsedArticle.dateModified - let authors = parsedAuthors(parsedArticle.authors) - let attachments = parsedAttachments(parsedArticle.enclosures) - - return ParsedItem(syncServiceID: nil, uniqueID: uniqueID, feedURL: parsedArticle.feedURL, url: url, externalURL: externalURL, title: title, contentHTML: contentHTML, contentText: nil, summary: nil, imageURL: nil, bannerImageURL: nil, datePublished: datePublished, dateModified: dateModified, authors: authors, tags: nil, attachments: attachments) - } - - static func parsedAuthors(_ authors: Set?) -> Set? { - - guard let authors = authors, !authors.isEmpty else { - return nil - } - - let transformedAuthors = authors.compactMap { (author) -> ParsedAuthor? in - return ParsedAuthor(name: author.name, url: author.url, avatarURL: nil, emailAddress: author.emailAddress) - } - - return transformedAuthors.isEmpty ? nil : Set(transformedAuthors) - } - - static func parsedAttachments(_ enclosures: Set?) -> Set? { - - guard let enclosures = enclosures, !enclosures.isEmpty else { - return nil - } - - let attachments = enclosures.compactMap { (enclosure) -> ParsedAttachment? in - - let sizeInBytes = enclosure.length > 0 ? enclosure.length : nil - return ParsedAttachment(url: enclosure.url, mimeType: enclosure.mimeType, title: nil, sizeInBytes: sizeInBytes, durationInSeconds: nil) - } - - return attachments.isEmpty ? nil : Set(attachments) - } -} diff --git a/Frameworks/RSParser/Feeds/XML/RSRSSParser.h b/Frameworks/RSParser/Feeds/XML/RSRSSParser.h deleted file mode 100755 index 26e97d0a2..000000000 --- a/Frameworks/RSParser/Feeds/XML/RSRSSParser.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// RSRSSParser.h -// RSParser -// -// Created by Brent Simmons on 1/6/15. -// Copyright (c) 2015 Ranchero Software LLC. All rights reserved. -// - -@import Foundation; - -@class ParserData; -@class RSParsedFeed; - -@interface RSRSSParser : NSObject - -+ (RSParsedFeed *)parseFeedWithData:(ParserData *)parserData; - - -@end diff --git a/Frameworks/RSParser/Feeds/XML/RSRSSParser.m b/Frameworks/RSParser/Feeds/XML/RSRSSParser.m deleted file mode 100755 index 0ff49471c..000000000 --- a/Frameworks/RSParser/Feeds/XML/RSRSSParser.m +++ /dev/null @@ -1,473 +0,0 @@ -// -// RSRSSParser.m -// RSParser -// -// Created by Brent Simmons on 1/6/15. -// Copyright (c) 2015 Ranchero Software LLC. All rights reserved. -// - -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import - -@interface RSRSSParser () - -@property (nonatomic) NSData *feedData; -@property (nonatomic) NSString *urlString; -@property (nonatomic) NSDictionary *currentAttributes; -@property (nonatomic) RSSAXParser *parser; -@property (nonatomic) NSMutableArray *articles; -@property (nonatomic) BOOL parsingArticle; -@property (nonatomic, readonly) RSParsedArticle *currentArticle; -@property (nonatomic) BOOL parsingChannelImage; -@property (nonatomic, readonly) NSDate *currentDate; -@property (nonatomic) BOOL endRSSFound; -@property (nonatomic) NSString *link; -@property (nonatomic) NSString *title; -@property (nonatomic) NSDate *dateParsed; -@property (nonatomic) BOOL isRDF; - -@end - - -@implementation RSRSSParser - -#pragma mark - Class Methods - -+ (RSParsedFeed *)parseFeedWithData:(ParserData *)parserData { - - RSRSSParser *parser = [[[self class] alloc] initWithParserData:parserData]; - return [parser parseFeed]; -} - -#pragma mark - Init - -- (instancetype)initWithParserData:(ParserData *)parserData { - - self = [super init]; - if (!self) { - return nil; - } - - _feedData = parserData.data; - _urlString = parserData.url; - _parser = [[RSSAXParser alloc] initWithDelegate:self]; - _articles = [NSMutableArray new]; - - return self; -} - - -#pragma mark - API - -- (RSParsedFeed *)parseFeed { - - [self parse]; - - RSParsedFeed *parsedFeed = [[RSParsedFeed alloc] initWithURLString:self.urlString title:self.title link:self.link articles:self.articles]; - - return parsedFeed; -} - - -#pragma mark - Constants - -static NSString *kIsPermaLinkKey = @"isPermaLink"; -static NSString *kURLKey = @"url"; -static NSString *kLengthKey = @"length"; -static NSString *kTypeKey = @"type"; -static NSString *kFalseValue = @"false"; -static NSString *kTrueValue = @"true"; -static NSString *kContentEncodedKey = @"content:encoded"; -static NSString *kDCDateKey = @"dc:date"; -static NSString *kDCCreatorKey = @"dc:creator"; -static NSString *kRDFAboutKey = @"rdf:about"; - -static const char *kItem = "item"; -static const NSInteger kItemLength = 5; - -static const char *kImage = "image"; -static const NSInteger kImageLength = 6; - -static const char *kLink = "link"; -static const NSInteger kLinkLength = 5; - -static const char *kTitle = "title"; -static const NSInteger kTitleLength = 6; - -static const char *kDC = "dc"; -static const NSInteger kDCLength = 3; - -static const char *kCreator = "creator"; -static const NSInteger kCreatorLength = 8; - -static const char *kDate = "date"; -static const NSInteger kDateLength = 5; - -static const char *kContent = "content"; -static const NSInteger kContentLength = 8; - -static const char *kEncoded = "encoded"; -static const NSInteger kEncodedLength = 8; - -static const char *kGuid = "guid"; -static const NSInteger kGuidLength = 5; - -static const char *kPubDate = "pubDate"; -static const NSInteger kPubDateLength = 8; - -static const char *kAuthor = "author"; -static const NSInteger kAuthorLength = 7; - -static const char *kDescription = "description"; -static const NSInteger kDescriptionLength = 12; - -static const char *kRSS = "rss"; -static const NSInteger kRSSLength = 4; - -static const char *kURL = "url"; -static const NSInteger kURLLength = 4; - -static const char *kLength = "length"; -static const NSInteger kLengthLength = 7; - -static const char *kType = "type"; -static const NSInteger kTypeLength = 5; - -static const char *kIsPermaLink = "isPermaLink"; -static const NSInteger kIsPermaLinkLength = 12; - -static const char *kRDF = "rdf"; -static const NSInteger kRDFlength = 4; - -static const char *kAbout = "about"; -static const NSInteger kAboutLength = 6; - -static const char *kFalse = "false"; -static const NSInteger kFalseLength = 6; - -static const char *kTrue = "true"; -static const NSInteger kTrueLength = 5; - -static const char *kUppercaseRDF = "RDF"; -static const NSInteger kUppercaseRDFLength = 4; - -static const char *kEnclosure = "enclosure"; -static const NSInteger kEnclosureLength = 10; - -#pragma mark - Parsing - -- (void)parse { - - self.dateParsed = [NSDate date]; - - @autoreleasepool { - [self.parser parseData:self.feedData]; - [self.parser finishParsing]; - } -} - - -- (void)addArticle { - - RSParsedArticle *article = [[RSParsedArticle alloc] initWithFeedURL:self.urlString]; - article.dateParsed = self.dateParsed; - - [self.articles addObject:article]; -} - - -- (RSParsedArticle *)currentArticle { - - return self.articles.lastObject; -} - - -- (void)addFeedElement:(const xmlChar *)localName prefix:(const xmlChar *)prefix { - - if (prefix != NULL) { - return; - } - - if (RSSAXEqualTags(localName, kLink, kLinkLength)) { - if (!self.link) { - self.link = self.parser.currentStringWithTrimmedWhitespace; - } - } - - else if (RSSAXEqualTags(localName, kTitle, kTitleLength)) { - self.title = self.parser.currentStringWithTrimmedWhitespace; - } -} - -- (void)addAuthorWithString:(NSString *)authorString { - - if (RSParserStringIsEmpty(authorString)) { - return; - } - - RSParsedAuthor *author = [RSParsedAuthor authorWithSingleString:self.parser.currentStringWithTrimmedWhitespace]; - [self.currentArticle addAuthor:author]; -} - -- (void)addDCElement:(const xmlChar *)localName { - - if (RSSAXEqualTags(localName, kCreator, kCreatorLength)) { - [self addAuthorWithString:self.parser.currentStringWithTrimmedWhitespace]; - } - else if (RSSAXEqualTags(localName, kDate, kDateLength)) { - self.currentArticle.datePublished = self.currentDate; - } -} - - -- (void)addGuid { - - self.currentArticle.guid = self.parser.currentStringWithTrimmedWhitespace; - - NSString *isPermaLinkValue = [self.currentAttributes rsparser_objectForCaseInsensitiveKey:@"ispermalink"]; - if (!isPermaLinkValue || ![isPermaLinkValue isEqualToString:@"false"]) { - self.currentArticle.permalink = [self urlString:self.currentArticle.guid]; - } -} - -- (void)addEnclosure { - - NSDictionary *attributes = self.currentAttributes; - NSString *url = attributes[kURLKey]; - if (!url || url.length < 1) { - return; - } - - RSParsedEnclosure *enclosure = [[RSParsedEnclosure alloc] init]; - enclosure.url = url; - enclosure.length = [attributes[kLengthKey] integerValue]; - enclosure.mimeType = attributes[kTypeKey]; - - [self.currentArticle addEnclosure:enclosure]; -} - -- (NSString *)urlString:(NSString *)s { - - /*Resolve against home page URL (if available) or feed URL.*/ - - if ([[s lowercaseString] hasPrefix:@"http"]) { - return s; - } - - if (!self.link) { - //TODO: get feed URL and use that to resolve URL.*/ - return s; - } - - NSURL *baseURL = [NSURL URLWithString:self.link]; - if (!baseURL) { - return s; - } - - NSURL *resolvedURL = [NSURL URLWithString:s relativeToURL:baseURL]; - if (resolvedURL.absoluteString) { - return resolvedURL.absoluteString; - } - - return s; -} - - -- (NSString *)currentStringWithHTMLEntitiesDecoded { - - return [self.parser.currentStringWithTrimmedWhitespace rsparser_stringByDecodingHTMLEntities]; -} - -- (void)addArticleElement:(const xmlChar *)localName prefix:(const xmlChar *)prefix { - - if (RSSAXEqualTags(prefix, kDC, kDCLength)) { - - [self addDCElement:localName]; - return; - } - - if (RSSAXEqualTags(prefix, kContent, kContentLength) && RSSAXEqualTags(localName, kEncoded, kEncodedLength)) { - - self.currentArticle.body = [self currentStringWithHTMLEntitiesDecoded]; - return; - } - - if (prefix != NULL) { - return; - } - - if (RSSAXEqualTags(localName, kGuid, kGuidLength)) { - [self addGuid]; - } - else if (RSSAXEqualTags(localName, kPubDate, kPubDateLength)) { - self.currentArticle.datePublished = self.currentDate; - } - else if (RSSAXEqualTags(localName, kAuthor, kAuthorLength)) { - [self addAuthorWithString:self.parser.currentStringWithTrimmedWhitespace]; - } - else if (RSSAXEqualTags(localName, kLink, kLinkLength)) { - self.currentArticle.link = [self urlString:self.parser.currentStringWithTrimmedWhitespace]; - } - else if (RSSAXEqualTags(localName, kDescription, kDescriptionLength)) { - - if (!self.currentArticle.body) { - self.currentArticle.body = [self currentStringWithHTMLEntitiesDecoded]; - } - } - else if (RSSAXEqualTags(localName, kTitle, kTitleLength)) { - self.currentArticle.title = [self currentStringWithHTMLEntitiesDecoded]; - } - else if (RSSAXEqualTags(localName, kEnclosure, kEnclosureLength)) { - [self addEnclosure]; - } -} - - -- (NSDate *)currentDate { - - return RSDateWithBytes(self.parser.currentCharacters.bytes, self.parser.currentCharacters.length); -} - - -#pragma mark - RSSAXParserDelegate - -- (void)saxParser:(RSSAXParser *)SAXParser XMLStartElement:(const xmlChar *)localName prefix:(const xmlChar *)prefix uri:(const xmlChar *)uri numberOfNamespaces:(NSInteger)numberOfNamespaces namespaces:(const xmlChar **)namespaces numberOfAttributes:(NSInteger)numberOfAttributes numberDefaulted:(int)numberDefaulted attributes:(const xmlChar **)attributes { - - if (self.endRSSFound) { - return; - } - - if (RSSAXEqualTags(localName, kUppercaseRDF, kUppercaseRDFLength)) { - self.isRDF = YES; - return; - } - - NSDictionary *xmlAttributes = nil; - if ((self.isRDF && RSSAXEqualTags(localName, kItem, kItemLength)) || RSSAXEqualTags(localName, kGuid, kGuidLength) || RSSAXEqualTags(localName, kEnclosure, kEnclosureLength)) { - xmlAttributes = [self.parser attributesDictionary:attributes numberOfAttributes:numberOfAttributes]; - } - if (self.currentAttributes != xmlAttributes) { - self.currentAttributes = xmlAttributes; - } - - if (!prefix && RSSAXEqualTags(localName, kItem, kItemLength)) { - - [self addArticle]; - self.parsingArticle = YES; - - if (self.isRDF && xmlAttributes && xmlAttributes[kRDFAboutKey]) { /*RSS 1.0 guid*/ - self.currentArticle.guid = xmlAttributes[kRDFAboutKey]; - self.currentArticle.permalink = self.currentArticle.guid; - } - } - - else if (!prefix && RSSAXEqualTags(localName, kImage, kImageLength)) { - self.parsingChannelImage = YES; - } - - if (!self.parsingChannelImage) { - [self.parser beginStoringCharacters]; - } -} - - -- (void)saxParser:(RSSAXParser *)SAXParser XMLEndElement:(const xmlChar *)localName prefix:(const xmlChar *)prefix uri:(const xmlChar *)uri { - - if (self.endRSSFound) { - return; - } - - if (self.isRDF && RSSAXEqualTags(localName, kUppercaseRDF, kUppercaseRDFLength)) { - self.endRSSFound = YES; - } - - else if (RSSAXEqualTags(localName, kRSS, kRSSLength)) { - self.endRSSFound = YES; - } - - else if (RSSAXEqualTags(localName, kImage, kImageLength)) { - self.parsingChannelImage = NO; - } - - else if (RSSAXEqualTags(localName, kItem, kItemLength)) { - self.parsingArticle = NO; - } - - else if (self.parsingArticle) { - [self addArticleElement:localName prefix:prefix]; - } - - else if (!self.parsingChannelImage) { - [self addFeedElement:localName prefix:prefix]; - } -} - - -- (NSString *)saxParser:(RSSAXParser *)SAXParser internedStringForName:(const xmlChar *)name prefix:(const xmlChar *)prefix { - - if (RSSAXEqualTags(prefix, kRDF, kRDFlength)) { - - if (RSSAXEqualTags(name, kAbout, kAboutLength)) { - return kRDFAboutKey; - } - - return nil; - } - - if (prefix) { - return nil; - } - - if (RSSAXEqualTags(name, kIsPermaLink, kIsPermaLinkLength)) { - return kIsPermaLinkKey; - } - - if (RSSAXEqualTags(name, kURL, kURLLength)) { - return kURLKey; - } - - if (RSSAXEqualTags(name, kLength, kLengthLength)) { - return kLengthKey; - } - - if (RSSAXEqualTags(name, kType, kTypeLength)) { - return kTypeKey; - } - - return nil; -} - - -static BOOL equalBytes(const void *bytes1, const void *bytes2, NSUInteger length) { - - return memcmp(bytes1, bytes2, length) == 0; -} - - -- (NSString *)saxParser:(RSSAXParser *)SAXParser internedStringForValue:(const void *)bytes length:(NSUInteger)length { - - static const NSUInteger falseLength = kFalseLength - 1; - static const NSUInteger trueLength = kTrueLength - 1; - - if (length == falseLength && equalBytes(bytes, kFalse, falseLength)) { - return kFalseValue; - } - - if (length == trueLength && equalBytes(bytes, kTrue, trueLength)) { - return kTrueValue; - } - - return nil; -} - - -@end diff --git a/Frameworks/RSParser/Feeds/XML/RSSParser.swift b/Frameworks/RSParser/Feeds/XML/RSSParser.swift deleted file mode 100644 index 885790e16..000000000 --- a/Frameworks/RSParser/Feeds/XML/RSSParser.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// RSSParser.swift -// RSParser -// -// Created by Brent Simmons on 6/25/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -// RSSParser wraps the Objective-C RSRSSParser. -// -// The Objective-C parser creates RSParsedFeed, RSParsedArticle, etc. -// This wrapper then creates ParsedFeed, ParsedItem, etc. so that it creates -// the same things that JSONFeedParser and RSSInJSONParser create. -// -// In general, you should see FeedParser.swift for all your feed-parsing needs. - -public struct RSSParser { - - public static func parse(_ parserData: ParserData) -> ParsedFeed? { - - if let rsParsedFeed = RSRSSParser.parseFeed(with: parserData) { - return RSParsedFeedTransformer.parsedFeed(rsParsedFeed) - } - return nil - } -} diff --git a/Frameworks/RSParser/HTML/RSHTMLLinkParser.h b/Frameworks/RSParser/HTML/RSHTMLLinkParser.h deleted file mode 100755 index eae931b16..000000000 --- a/Frameworks/RSParser/HTML/RSHTMLLinkParser.h +++ /dev/null @@ -1,31 +0,0 @@ -// -// RSHTMLLinkParser.h -// RSParser -// -// Created by Brent Simmons on 8/7/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -@import Foundation; - -/*Returns all some_text as RSHTMLLink object array.*/ - -@class ParserData; -@class RSHTMLLink; - -@interface RSHTMLLinkParser : NSObject - -+ (NSArray *)htmlLinksWithParserData:(ParserData *)parserData; - -@end - - -@interface RSHTMLLink : NSObject - -// Any of these, even urlString, may be nil, because HTML can be bad. - -@property (nonatomic, readonly) NSString *urlString; //absolute -@property (nonatomic, readonly) NSString *text; -@property (nonatomic, readonly) NSString *title; //title attribute inside anchor tag - -@end diff --git a/Frameworks/RSParser/HTML/RSHTMLLinkParser.m b/Frameworks/RSParser/HTML/RSHTMLLinkParser.m deleted file mode 100755 index c6e7bd752..000000000 --- a/Frameworks/RSParser/HTML/RSHTMLLinkParser.m +++ /dev/null @@ -1,151 +0,0 @@ -// -// RSHTMLLinkParser.m -// RSParser -// -// Created by Brent Simmons on 8/7/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -#import -#import -#import -#import -#import -#import - - -@interface RSHTMLLinkParser() - -@property (nonatomic, readonly) NSMutableArray *links; -@property (nonatomic, readonly) ParserData *parserData; -@property (nonatomic, readonly) NSMutableArray *dictionaries; -@property (nonatomic, readonly) NSURL *baseURL; - -@end - - -@interface RSHTMLLink() - -@property (nonatomic, readwrite) NSString *urlString; //absolute -@property (nonatomic, readwrite) NSString *text; -@property (nonatomic, readwrite) NSString *title; //title attribute inside anchor tag - -@end - - -@implementation RSHTMLLinkParser - - -#pragma mark - Class Methods - -+ (NSArray *)htmlLinksWithParserData:(ParserData *)parserData { - - RSHTMLLinkParser *parser = [[self alloc] initWithParserData:parserData]; - return parser.links; -} - - -#pragma mark - Init - -- (instancetype)initWithParserData:(ParserData *)parserData { - - NSParameterAssert(parserData.data); - NSParameterAssert(parserData.url); - - self = [super init]; - if (!self) { - return nil; - } - - _links = [NSMutableArray new]; - _parserData = parserData; - _dictionaries = [NSMutableArray new]; - _baseURL = [NSURL URLWithString:parserData.url]; - - [self parse]; - - return self; -} - - -#pragma mark - Parse - -- (void)parse { - - RSSAXHTMLParser *parser = [[RSSAXHTMLParser alloc] initWithDelegate:self]; - [parser parseData:self.parserData.data]; - [parser finishParsing]; -} - - -- (RSHTMLLink *)currentLink { - - return self.links.lastObject; -} - - -static NSString *kHrefKey = @"href"; - -- (NSString *)urlStringFromDictionary:(NSDictionary *)d { - - NSString *href = [d rsparser_objectForCaseInsensitiveKey:kHrefKey]; - if (!href) { - return nil; - } - - NSURL *absoluteURL = [NSURL URLWithString:href relativeToURL:self.baseURL]; - return absoluteURL.absoluteString; -} - - -static NSString *kTitleKey = @"title"; - -- (NSString *)titleFromDictionary:(NSDictionary *)d { - - return [d rsparser_objectForCaseInsensitiveKey:kTitleKey]; -} - - -- (void)handleLinkAttributes:(NSDictionary *)d { - - RSHTMLLink *link = self.currentLink; - link.urlString = [self urlStringFromDictionary:d]; - link.title = [self titleFromDictionary:d]; -} - - -static const char *kAnchor = "a"; -static const NSInteger kAnchorLength = 2; - -- (void)saxParser:(RSSAXHTMLParser *)SAXParser XMLStartElement:(const xmlChar *)localName attributes:(const xmlChar **)attributes { - - if (!RSSAXEqualTags(localName, kAnchor, kAnchorLength)) { - return; - } - - RSHTMLLink *link = [RSHTMLLink new]; - [self.links addObject:link]; - - NSDictionary *d = [SAXParser attributesDictionary:attributes]; - if (!RSParserObjectIsEmpty(d)) { - [self handleLinkAttributes:d]; - } - - [SAXParser beginStoringCharacters]; -} - - -- (void)saxParser:(RSSAXParser *)SAXParser XMLEndElement:(const xmlChar *)localName { - - if (!RSSAXEqualTags(localName, kAnchor, kAnchorLength)) { - return; - } - - self.currentLink.text = SAXParser.currentStringWithTrimmedWhitespace; -} - -@end - -@implementation RSHTMLLink - -@end diff --git a/Frameworks/RSParser/HTML/RSHTMLMetadata.h b/Frameworks/RSParser/HTML/RSHTMLMetadata.h deleted file mode 100755 index bfcb5e2a5..000000000 --- a/Frameworks/RSParser/HTML/RSHTMLMetadata.h +++ /dev/null @@ -1,84 +0,0 @@ -// -// RSHTMLMetadata.h -// RSParser -// -// Created by Brent Simmons on 3/6/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -@import Foundation; - -@class RSHTMLMetadataFeedLink; -@class RSHTMLMetadataAppleTouchIcon; -@class RSHTMLOpenGraphProperties; -@class RSHTMLOpenGraphImage; -@class RSHTMLTag; -@class RSHTMLTwitterProperties; - -@interface RSHTMLMetadata : NSObject - -- (instancetype)initWithURLString:(NSString *)urlString tags:(NSArray *)tags; - -@property (nonatomic, readonly) NSString *baseURLString; -@property (nonatomic, readonly) NSArray *tags; - -@property (nonatomic, readonly) NSString *faviconLink; -@property (nonatomic, readonly) NSArray *appleTouchIcons; -@property (nonatomic, readonly) NSArray *feedLinks; - -@property (nonatomic, readonly) RSHTMLOpenGraphProperties *openGraphProperties; -@property (nonatomic, readonly) RSHTMLTwitterProperties *twitterProperties; - -@end - - -@interface RSHTMLMetadataAppleTouchIcon : NSObject - -@property (nonatomic, readonly) NSString *rel; -@property (nonatomic, readonly) NSString *sizes; -@property (nonatomic, readonly) CGSize size; -@property (nonatomic, readonly) NSString *urlString; // Absolute. - -@end - - -@interface RSHTMLMetadataFeedLink : NSObject - -@property (nonatomic, readonly) NSString *title; -@property (nonatomic, readonly) NSString *type; -@property (nonatomic, readonly) NSString *urlString; // Absolute. - -@end - -@interface RSHTMLOpenGraphProperties : NSObject - -// TODO: the rest. At this writing (Nov. 26, 2017) I just care about og:image. -// See http://ogp.me/ - -- (instancetype)initWithURLString:(NSString *)urlString tags:(NSArray *)tags; - -@property (nonatomic, readonly) NSArray *images; - -@end - -@interface RSHTMLOpenGraphImage : NSObject - -@property (nonatomic, readonly) NSString *url; -@property (nonatomic, readonly) NSString *secureURL; -@property (nonatomic, readonly) NSString *mimeType; -@property (nonatomic, readonly) CGFloat width; -@property (nonatomic, readonly) CGFloat height; -@property (nonatomic, readonly) NSString *altText; - -@end - -@interface RSHTMLTwitterProperties : NSObject - -// TODO: the rest. At this writing (Nov. 26, 2017) I just care about twitter:image:src. - -- (instancetype)initWithURLString:(NSString *)urlString tags:(NSArray *)tags; - -@property (nonatomic, readonly) NSString *imageURL; // twitter:image:src - -@end - diff --git a/Frameworks/RSParser/HTML/RSHTMLMetadata.m b/Frameworks/RSParser/HTML/RSHTMLMetadata.m deleted file mode 100755 index 882144101..000000000 --- a/Frameworks/RSParser/HTML/RSHTMLMetadata.m +++ /dev/null @@ -1,430 +0,0 @@ -// -// RSHTMLMetadata.m -// RSParser -// -// Created by Brent Simmons on 3/6/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -#import -#import -#import - -static NSString *urlStringFromDictionary(NSDictionary *d); -static NSString *absoluteURLStringWithRelativeURLString(NSString *relativeURLString, NSString *baseURLString); -static NSString *absoluteURLStringWithDictionary(NSDictionary *d, NSString *baseURLString); -static NSArray *objectsOfClassWithTags(Class class, NSArray *tags, NSString *baseURLString); -static NSString *relValue(NSDictionary *d); -static BOOL typeIsFeedType(NSString *type); - -static NSString *kShortcutIconRelValue = @"shortcut icon"; -static NSString *kIconRelValue = @"icon"; -static NSString *kHrefKey = @"href"; -static NSString *kSrcKey = @"src"; -static NSString *kAppleTouchIconValue = @"apple-touch-icon"; -static NSString *kAppleTouchIconPrecomposedValue = @"apple-touch-icon-precomposed"; -static NSString *kSizesKey = @"sizes"; -static NSString *kTitleKey = @"title"; -static NSString *kRelKey = @"rel"; -static NSString *kAlternateKey = @"alternate"; -static NSString *kRSSSuffix = @"/rss+xml"; -static NSString *kAtomSuffix = @"/atom+xml"; -static NSString *kJSONSuffix = @"/json"; -static NSString *kTypeKey = @"type"; - -@interface RSHTMLMetadataAppleTouchIcon () - -- (instancetype)initWithTag:(RSHTMLTag *)tag baseURLString:(NSString *)baseURLString; - -@end - - -@interface RSHTMLMetadataFeedLink () - -- (instancetype)initWithTag:(RSHTMLTag *)tag baseURLString:(NSString *)baseURLString; - -@end - - -@implementation RSHTMLMetadata - -#pragma mark - Init - -- (instancetype)initWithURLString:(NSString *)urlString tags:(NSArray *)tags { - - self = [super init]; - if (!self) { - return nil; - } - - _baseURLString = urlString; - _tags = tags; - _faviconLink = [self resolvedLinkFromFirstLinkTagWithMatchingRel:kShortcutIconRelValue]; - if (_faviconLink == nil) { - _faviconLink = [self resolvedLinkFromFirstLinkTagWithMatchingRel:kIconRelValue]; - } - - NSArray *appleTouchIconTags = [self appleTouchIconTags]; - _appleTouchIcons = objectsOfClassWithTags([RSHTMLMetadataAppleTouchIcon class], appleTouchIconTags, urlString); - - NSArray *feedLinkTags = [self feedLinkTags]; - _feedLinks = objectsOfClassWithTags([RSHTMLMetadataFeedLink class], feedLinkTags, urlString); - - _openGraphProperties = [[RSHTMLOpenGraphProperties alloc] initWithURLString:urlString tags:tags]; - _twitterProperties = [[RSHTMLTwitterProperties alloc] initWithURLString:urlString tags:tags]; - - return self; -} - - -#pragma mark - Private - -- (RSHTMLTag *)firstLinkTagWithMatchingRel:(NSString *)valueToMatch { - - // Case-insensitive. - - for (RSHTMLTag *tag in self.tags) { - - if (tag.type != RSHTMLTagTypeLink) { - continue; - } - NSString *oneRelValue = relValue(tag.attributes); - if (oneRelValue && [oneRelValue compare:valueToMatch options:NSCaseInsensitiveSearch] == NSOrderedSame) { - return tag; - } - } - - return nil; -} - - -- (NSArray *)appleTouchIconTags { - - NSMutableArray *tags = [NSMutableArray new]; - - for (RSHTMLTag *tag in self.tags) { - - if (tag.type != RSHTMLTagTypeLink) { - continue; - } - NSString *oneRelValue = relValue(tag.attributes).lowercaseString; - if ([oneRelValue isEqualToString:kAppleTouchIconValue] || [oneRelValue isEqualToString:kAppleTouchIconPrecomposedValue]) { - [tags addObject:tag]; - } - } - - return tags; -} - - -- (NSArray *)feedLinkTags { - - NSMutableArray *tags = [NSMutableArray new]; - - for (RSHTMLTag *tag in self.tags) { - - if (tag.type != RSHTMLTagTypeLink) { - continue; - } - - NSDictionary *oneDictionary = tag.attributes; - NSString *oneRelValue = relValue(oneDictionary).lowercaseString; - if (![oneRelValue isEqualToString:kAlternateKey]) { - continue; - } - - NSString *oneType = [oneDictionary rsparser_objectForCaseInsensitiveKey:kTypeKey]; - if (!typeIsFeedType(oneType)) { - continue; - } - - if (RSParserStringIsEmpty(urlStringFromDictionary(oneDictionary))) { - continue; - } - - [tags addObject:tag]; - } - - return tags; -} - - -- (NSString *)resolvedLinkFromFirstLinkTagWithMatchingRel:(NSString *)relValue { - - RSHTMLTag *tag = [self firstLinkTagWithMatchingRel:relValue]; - return absoluteURLStringWithDictionary(tag.attributes, self.baseURLString); -} - -@end - - -static NSString *relValue(NSDictionary *d) { - - return [d rsparser_objectForCaseInsensitiveKey:kRelKey]; -} - - -static NSString *urlStringFromDictionary(NSDictionary *d) { - - NSString *urlString = [d rsparser_objectForCaseInsensitiveKey:kHrefKey]; - if (urlString) { - return urlString; - } - - return [d rsparser_objectForCaseInsensitiveKey:kSrcKey]; -} - - -static NSString *absoluteURLStringWithRelativeURLString(NSString *relativeURLString, NSString *baseURLString) { - - NSURL *url = [NSURL URLWithString:baseURLString]; - if (!url) { - return nil; - } - - NSURL *absoluteURL = [NSURL URLWithString:relativeURLString relativeToURL:url]; - return absoluteURL.absoluteString; -} - - -static NSString *absoluteURLStringWithDictionary(NSDictionary *d, NSString *baseURLString) { - - NSString *urlString = urlStringFromDictionary(d); - if (RSParserStringIsEmpty(urlString)) { - return nil; - } - return absoluteURLStringWithRelativeURLString(urlString, baseURLString); -} - - -static NSArray *objectsOfClassWithTags(Class class, NSArray *tags, NSString *baseURLString) { - - NSMutableArray *objects = [NSMutableArray new]; - - for (RSHTMLTag *tag in tags) { - - id oneObject = [[class alloc] initWithTag:tag baseURLString:baseURLString]; - if (oneObject) { - [objects addObject:oneObject]; - } - } - - return objects; -} - - -static BOOL typeIsFeedType(NSString *type) { - - type = type.lowercaseString; - return [type hasSuffix:kRSSSuffix] || [type hasSuffix:kAtomSuffix] || [type hasSuffix:kJSONSuffix]; -} - - -@implementation RSHTMLMetadataAppleTouchIcon - -- (instancetype)initWithTag:(RSHTMLTag *)tag baseURLString:(NSString *)baseURLString { - - self = [super init]; - if (!self) { - return nil; - } - - NSDictionary *d = tag.attributes; - _urlString = absoluteURLStringWithDictionary(d, baseURLString); - _sizes = [d rsparser_objectForCaseInsensitiveKey:kSizesKey]; - _rel = [d rsparser_objectForCaseInsensitiveKey:kRelKey]; - - _size = CGSizeZero; - if (_sizes) { - NSArray *components = [_sizes componentsSeparatedByString:@"x"]; - if (components.count == 2) { - CGFloat width = [components[0] floatValue]; - CGFloat height = [components[1] floatValue]; - _size = CGSizeMake(width, height); - } - } - - return self; -} - -@end - - -@implementation RSHTMLMetadataFeedLink - -- (instancetype)initWithTag:(RSHTMLTag *)tag baseURLString:(NSString *)baseURLString { - - self = [super init]; - if (!self) { - return nil; - } - - NSDictionary *d = tag.attributes; - _urlString = absoluteURLStringWithDictionary(d, baseURLString); - _title = [d rsparser_objectForCaseInsensitiveKey:kTitleKey]; - _type = [d rsparser_objectForCaseInsensitiveKey:kTypeKey]; - - return self; -} - -@end - -@interface RSHTMLOpenGraphImage () - -@property (nonatomic, readwrite) NSString *url; -@property (nonatomic, readwrite) NSString *secureURL; -@property (nonatomic, readwrite) NSString *mimeType; -@property (nonatomic, readwrite) CGFloat width; -@property (nonatomic, readwrite) CGFloat height; -@property (nonatomic, readwrite) NSString *altText; - -@end - -@implementation RSHTMLOpenGraphImage - - -@end - -@interface RSHTMLOpenGraphProperties () - -@property (nonatomic) NSMutableArray *ogImages; -@end - -@implementation RSHTMLOpenGraphProperties - -- (instancetype)initWithURLString:(NSString *)urlString tags:(NSArray *)tags { - - self = [super init]; - if (!self) { - return nil; - } - - _ogImages = [NSMutableArray new]; - - [self parseTags:tags]; - return self; -} - - -- (RSHTMLOpenGraphImage *)currentImage { - - return self.ogImages.lastObject; -} - - -- (RSHTMLOpenGraphImage *)pushImage { - - RSHTMLOpenGraphImage *image = [RSHTMLOpenGraphImage new]; - [self.ogImages addObject:image]; - return image; -} - -- (RSHTMLOpenGraphImage *)ensureImage { - - RSHTMLOpenGraphImage *image = [self currentImage]; - if (image != nil) { - return image; - } - return [self pushImage]; -} - - -- (NSArray *)images { - - return self.ogImages; -} - -static NSString *ogPrefix = @"og:"; -static NSString *ogImage = @"og:image"; -static NSString *ogImageURL = @"og:image:url"; -static NSString *ogImageSecureURL = @"og:image:secure_url"; -static NSString *ogImageType = @"og:image:type"; -static NSString *ogImageWidth = @"og:image:width"; -static NSString *ogImageHeight = @"og:image:height"; -static NSString *ogImageAlt = @"og:image:alt"; -static NSString *ogPropertyKey = @"property"; -static NSString *ogContentKey = @"content"; - -- (void)parseTags:(NSArray *)tags { - - for (RSHTMLTag *tag in tags) { - - if (tag.type != RSHTMLTagTypeMeta) { - continue; - } - - NSString *propertyName = tag.attributes[ogPropertyKey]; - if (!propertyName || ![propertyName hasPrefix:ogPrefix]) { - continue; - } - NSString *content = tag.attributes[ogContentKey]; - if (!content) { - continue; - } - - if ([propertyName isEqualToString:ogImage]) { - RSHTMLOpenGraphImage *image = [self currentImage]; - if (!image || image.url) { // Most likely case, since og:image will probably appear before other image attributes. - image = [self pushImage]; - } - image.url = content; - } - - else if ([propertyName isEqualToString:ogImageURL]) { - [self ensureImage].url = content; - } - else if ([propertyName isEqualToString:ogImageSecureURL]) { - [self ensureImage].secureURL = content; - } - else if ([propertyName isEqualToString:ogImageType]) { - [self ensureImage].mimeType = content; - } - else if ([propertyName isEqualToString:ogImageAlt]) { - [self ensureImage].altText = content; - } - else if ([propertyName isEqualToString:ogImageWidth]) { - [self ensureImage].width = [content floatValue]; - } - else if ([propertyName isEqualToString:ogImageHeight]) { - [self ensureImage].height = [content floatValue]; - } - } -} - -@end - -@implementation RSHTMLTwitterProperties - -static NSString *twitterNameKey = @"name"; -static NSString *twitterContentKey = @"content"; -static NSString *twitterImageSrc = @"twitter:image:src"; - -- (instancetype)initWithURLString:(NSString *)urlString tags:(NSArray *)tags { - - self = [super init]; - if (!self) { - return nil; - } - - for (RSHTMLTag *tag in tags) { - - if (tag.type != RSHTMLTagTypeMeta) { - continue; - } - NSString *name = tag.attributes[twitterNameKey]; - if (!name || ![name isEqualToString:twitterImageSrc]) { - continue; - } - NSString *content = tag.attributes[twitterContentKey]; - if (!content || content.length < 1) { - continue; - } - _imageURL = content; - break; - } - - return self; -} - -@end - diff --git a/Frameworks/RSParser/HTML/RSHTMLMetadataParser.h b/Frameworks/RSParser/HTML/RSHTMLMetadataParser.h deleted file mode 100755 index f9361905c..000000000 --- a/Frameworks/RSParser/HTML/RSHTMLMetadataParser.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// RSHTMLMetadataParser.h -// RSParser -// -// Created by Brent Simmons on 3/6/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -@import Foundation; - - -@class RSHTMLMetadata; -@class ParserData; - -NS_ASSUME_NONNULL_BEGIN - -@interface RSHTMLMetadataParser : NSObject - -+ (RSHTMLMetadata *)HTMLMetadataWithParserData:(ParserData *)parserData; - - -@end - -NS_ASSUME_NONNULL_END diff --git a/Frameworks/RSParser/HTML/RSHTMLMetadataParser.m b/Frameworks/RSParser/HTML/RSHTMLMetadataParser.m deleted file mode 100755 index d154cf4a9..000000000 --- a/Frameworks/RSParser/HTML/RSHTMLMetadataParser.m +++ /dev/null @@ -1,142 +0,0 @@ -// -// RSHTMLMetadataParser.m -// RSParser -// -// Created by Brent Simmons on 3/6/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -#import -#import -#import -#import -#import -#import -#import -#import - -@interface RSHTMLMetadataParser () - -@property (nonatomic, readonly) ParserData *parserData; -@property (nonatomic, readwrite) RSHTMLMetadata *metadata; -@property (nonatomic) NSMutableArray *tags; -@property (nonatomic) BOOL didFinishParsing; - -@end - - -@implementation RSHTMLMetadataParser - - -#pragma mark - Class Methods - -+ (RSHTMLMetadata *)HTMLMetadataWithParserData:(ParserData *)parserData { - - RSHTMLMetadataParser *parser = [[self alloc] initWithParserData:parserData]; - return parser.metadata; -} - - -#pragma mark - Init - -- (instancetype)initWithParserData:(ParserData *)parserData { - - NSParameterAssert(parserData.data); - NSParameterAssert(parserData.url); - - self = [super init]; - if (!self) { - return nil; - } - - _parserData = parserData; - _tags = [NSMutableArray new]; - - [self parse]; - - return self; -} - - -#pragma mark - Parse - -- (void)parse { - - RSSAXHTMLParser *parser = [[RSSAXHTMLParser alloc] initWithDelegate:self]; - [parser parseData:self.parserData.data]; - [parser finishParsing]; - - self.metadata = [[RSHTMLMetadata alloc] initWithURLString:self.parserData.url tags:self.tags]; -} - - -static NSString *kHrefKey = @"href"; -static NSString *kSrcKey = @"src"; -static NSString *kRelKey = @"rel"; - -- (NSString *)linkForDictionary:(NSDictionary *)d { - - NSString *link = [d rsparser_objectForCaseInsensitiveKey:kHrefKey]; - if (link) { - return link; - } - - return [d rsparser_objectForCaseInsensitiveKey:kSrcKey]; -} - -- (void)handleLinkAttributes:(NSDictionary *)d { - - if (RSParserStringIsEmpty([d rsparser_objectForCaseInsensitiveKey:kRelKey])) { - return; - } - if (RSParserStringIsEmpty([self linkForDictionary:d])) { - return; - } - - RSHTMLTag *tag = [RSHTMLTag linkTagWithAttributes:d]; - [self.tags addObject:tag]; -} - -- (void)handleMetaAttributes:(NSDictionary *)d { - - RSHTMLTag *tag = [RSHTMLTag metaTagWithAttributes:d]; - [self.tags addObject:tag]; -} - -#pragma mark - RSSAXHTMLParserDelegate - -static const char *kBody = "body"; -static const NSInteger kBodyLength = 5; -static const char *kLink = "link"; -static const NSInteger kLinkLength = 5; -static const char *kMeta = "meta"; -static const NSInteger kMetaLength = 5; - -- (void)saxParser:(RSSAXHTMLParser *)SAXParser XMLStartElement:(const xmlChar *)localName attributes:(const xmlChar **)attributes { - - if (self.didFinishParsing) { - return; - } - - if (RSSAXEqualTags(localName, kBody, kBodyLength)) { - self.didFinishParsing = YES; - return; - } - - if (RSSAXEqualTags(localName, kLink, kLinkLength)) { - NSDictionary *d = [SAXParser attributesDictionary:attributes]; - if (!RSParserObjectIsEmpty(d)) { - [self handleLinkAttributes:d]; - } - return; - } - - if (RSSAXEqualTags(localName, kMeta, kMetaLength)) { - NSDictionary *d = [SAXParser attributesDictionary:attributes]; - if (!RSParserObjectIsEmpty(d)) { - [self handleMetaAttributes:d]; - } - } -} - -@end diff --git a/Frameworks/RSParser/HTML/RSHTMLTag.h b/Frameworks/RSParser/HTML/RSHTMLTag.h deleted file mode 100644 index ca85be5dd..000000000 --- a/Frameworks/RSParser/HTML/RSHTMLTag.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// RSHTMLTag.h -// RSParser -// -// Created by Brent Simmons on 11/26/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -@import Foundation; - -extern NSString *RSHTMLTagNameLink; // @"link" -extern NSString *RSHTMLTagNameMeta; // @"meta" - -typedef NS_ENUM(NSInteger, RSHTMLTagType) { - RSHTMLTagTypeLink, - RSHTMLTagTypeMeta -}; - -@interface RSHTMLTag : NSObject - -- (instancetype)initWithType:(RSHTMLTagType)type attributes:(NSDictionary *)attributes; - -+ (RSHTMLTag *)linkTagWithAttributes:(NSDictionary *)attributes; -+ (RSHTMLTag *)metaTagWithAttributes:(NSDictionary *)attributes; - -@property (nonatomic, readonly) RSHTMLTagType type; -@property (nonatomic, readonly) NSDictionary *attributes; - -@end diff --git a/Frameworks/RSParser/HTML/RSHTMLTag.m b/Frameworks/RSParser/HTML/RSHTMLTag.m deleted file mode 100644 index 5267ba45b..000000000 --- a/Frameworks/RSParser/HTML/RSHTMLTag.m +++ /dev/null @@ -1,39 +0,0 @@ -// -// RSHTMLTag.m -// RSParser -// -// Created by Brent Simmons on 11/26/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -#import "RSHTMLTag.h" - -NSString *RSHTMLTagNameLink = @"link"; -NSString *RSHTMLTagNameMeta = @"meta"; - -@implementation RSHTMLTag - -- (instancetype)initWithType:(RSHTMLTagType)type attributes:(NSDictionary *)attributes { - - self = [super init]; - if (!self) { - return nil; - } - - _type = type; - _attributes = attributes; - - return self; -} - -+ (RSHTMLTag *)linkTagWithAttributes:(NSDictionary *)attributes { - - return [[self alloc] initWithType:RSHTMLTagTypeLink attributes:attributes]; -} - -+ (RSHTMLTag *)metaTagWithAttributes:(NSDictionary *)attributes { - - return [[self alloc] initWithType:RSHTMLTagTypeMeta attributes:attributes]; -} - -@end diff --git a/Frameworks/RSParser/HTML/RSSAXHTMLParser.h b/Frameworks/RSParser/HTML/RSSAXHTMLParser.h deleted file mode 100755 index a256606bd..000000000 --- a/Frameworks/RSParser/HTML/RSSAXHTMLParser.h +++ /dev/null @@ -1,49 +0,0 @@ -// -// RSSAXHTMLParser.h -// RSParser -// -// Created by Brent Simmons on 3/6/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -@import Foundation; - -@class RSSAXHTMLParser; - -@protocol RSSAXHTMLParserDelegate - -@optional - -- (void)saxParser:(RSSAXHTMLParser *)SAXParser XMLStartElement:(const unsigned char *)localName attributes:(const unsigned char **)attributes; - -- (void)saxParser:(RSSAXHTMLParser *)SAXParser XMLEndElement:(const unsigned char *)localName; - -- (void)saxParser:(RSSAXHTMLParser *)SAXParser XMLCharactersFound:(const unsigned char *)characters length:(NSUInteger)length; - -- (void)saxParserDidReachEndOfDocument:(RSSAXHTMLParser *)SAXParser; // If canceled, may not get called (but might). - -@end - - -@interface RSSAXHTMLParser : NSObject - - -- (instancetype)initWithDelegate:(id)delegate; - -- (void)parseData:(NSData *)data; -- (void)parseBytes:(const void *)bytes numberOfBytes:(NSUInteger)numberOfBytes; -- (void)finishParsing; -- (void)cancel; - -@property (nonatomic, strong, readonly) NSData *currentCharacters; // nil if not storing characters. UTF-8 encoded. -@property (nonatomic, strong, readonly) NSString *currentString; // Convenience to get string version of currentCharacters. -@property (nonatomic, strong, readonly) NSString *currentStringWithTrimmedWhitespace; - -- (void)beginStoringCharacters; // Delegate can call from XMLStartElement. Characters will be available in XMLEndElement as currentCharacters property. Storing characters is stopped after each XMLEndElement. - -// Delegate can call from within XMLStartElement. - -- (NSDictionary *)attributesDictionary:(const unsigned char **)attributes; - - -@end diff --git a/Frameworks/RSParser/HTML/RSSAXHTMLParser.m b/Frameworks/RSParser/HTML/RSSAXHTMLParser.m deleted file mode 100755 index ea9cb2f6e..000000000 --- a/Frameworks/RSParser/HTML/RSSAXHTMLParser.m +++ /dev/null @@ -1,315 +0,0 @@ -// -// RSSAXHTMLParser.m -// RSParser -// -// Created by Brent Simmons on 3/6/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -#import -#import -#import -#import -#import -#import - - -@interface RSSAXHTMLParser () - -@property (nonatomic) id delegate; -@property (nonatomic, assign) htmlParserCtxtPtr context; -@property (nonatomic, assign) BOOL storingCharacters; -@property (nonatomic) NSMutableData *characters; -@property (nonatomic) BOOL delegateRespondsToStartElementMethod; -@property (nonatomic) BOOL delegateRespondsToEndElementMethod; -@property (nonatomic) BOOL delegateRespondsToCharactersFoundMethod; -@property (nonatomic) BOOL delegateRespondsToEndOfDocumentMethod; - -@end - - -@implementation RSSAXHTMLParser - - -+ (void)initialize { - - RSSAXInitLibXMLParser(); -} - - -#pragma mark - Init - -- (instancetype)initWithDelegate:(id)delegate { - - self = [super init]; - if (self == nil) - return nil; - - _delegate = delegate; - - if ([_delegate respondsToSelector:@selector(saxParser:XMLStartElement:attributes:)]) { - _delegateRespondsToStartElementMethod = YES; - } - if ([_delegate respondsToSelector:@selector(saxParser:XMLEndElement:)]) { - _delegateRespondsToEndElementMethod = YES; - } - if ([_delegate respondsToSelector:@selector(saxParser:XMLCharactersFound:length:)]) { - _delegateRespondsToCharactersFoundMethod = YES; - } - if ([_delegate respondsToSelector:@selector(saxParserDidReachEndOfDocument:)]) { - _delegateRespondsToEndOfDocumentMethod = YES; - } - - return self; -} - - -#pragma mark - Dealloc - -- (void)dealloc { - - if (_context != nil) { - htmlFreeParserCtxt(_context); - _context = nil; - } - _delegate = nil; -} - - -#pragma mark - API - -static xmlSAXHandler saxHandlerStruct; - -- (void)parseData:(NSData *)data { - - [self parseBytes:data.bytes numberOfBytes:data.length]; -} - - -- (void)parseBytes:(const void *)bytes numberOfBytes:(NSUInteger)numberOfBytes { - - if (self.context == nil) { - - xmlCharEncoding characterEncoding = xmlDetectCharEncoding(bytes, (int)numberOfBytes); - self.context = htmlCreatePushParserCtxt(&saxHandlerStruct, (__bridge void *)self, nil, 0, nil, characterEncoding); - htmlCtxtUseOptions(self.context, XML_PARSE_RECOVER | XML_PARSE_NONET | HTML_PARSE_COMPACT); - } - - @autoreleasepool { - htmlParseChunk(self.context, (const char *)bytes, (int)numberOfBytes, 0); - } -} - - -- (void)finishParsing { - - NSAssert(self.context != nil, nil); - if (self.context == nil) - return; - - @autoreleasepool { - htmlParseChunk(self.context, nil, 0, 1); - htmlFreeParserCtxt(self.context); - self.context = nil; - self.characters = nil; - } -} - - -- (void)cancel { - - @autoreleasepool { - xmlStopParser(self.context); - } -} - - - -- (void)beginStoringCharacters { - self.storingCharacters = YES; - self.characters = [NSMutableData new]; -} - - -- (void)endStoringCharacters { - self.storingCharacters = NO; - self.characters = nil; -} - - -- (NSData *)currentCharacters { - - if (!self.storingCharacters) { - return nil; - } - - return self.characters; -} - - -- (NSString *)currentString { - - NSData *d = self.currentCharacters; - if (RSParserObjectIsEmpty(d)) { - return nil; - } - - return [[NSString alloc] initWithData:d encoding:NSUTF8StringEncoding]; -} - - -- (NSString *)currentStringWithTrimmedWhitespace { - - return [self.currentString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; -} - - -#pragma mark - Attributes Dictionary - -- (NSDictionary *)attributesDictionary:(const xmlChar **)attributes { - - if (!attributes) { - return nil; - } - - NSMutableDictionary *d = [NSMutableDictionary new]; - - NSInteger ix = 0; - NSString *currentKey = nil; - while (true) { - - const xmlChar *oneAttribute = attributes[ix]; - ix++; - - if (!currentKey && !oneAttribute) { - break; - } - - if (!currentKey) { - currentKey = [NSString stringWithUTF8String:(const char *)oneAttribute]; - } - else { - NSString *value = nil; - if (oneAttribute) { - value = [NSString stringWithUTF8String:(const char *)oneAttribute]; - } - - d[currentKey] = value ? value : @""; - currentKey = nil; - } - } - - return [d copy]; -} - - -#pragma mark - Callbacks - -- (void)xmlEndDocument { - - @autoreleasepool { - if (self.delegateRespondsToEndOfDocumentMethod) { - [self.delegate saxParserDidReachEndOfDocument:self]; - } - - [self endStoringCharacters]; - } -} - - -- (void)xmlCharactersFound:(const xmlChar *)ch length:(NSUInteger)length { - - @autoreleasepool { - if (self.storingCharacters) { - [self.characters appendBytes:(const void *)ch length:length]; - } - - if (self.delegateRespondsToCharactersFoundMethod) { - [self.delegate saxParser:self XMLCharactersFound:ch length:length]; - } - } -} - - -- (void)xmlStartElement:(const xmlChar *)localName attributes:(const xmlChar **)attributes { - - @autoreleasepool { - if (self.delegateRespondsToStartElementMethod) { - - [self.delegate saxParser:self XMLStartElement:localName attributes:attributes]; - } - } -} - - -- (void)xmlEndElement:(const xmlChar *)localName { - - @autoreleasepool { - if (self.delegateRespondsToEndElementMethod) { - [self.delegate saxParser:self XMLEndElement:localName]; - } - - [self endStoringCharacters]; - } -} - - -@end - - -static void startElementSAX(void *context, const xmlChar *localname, const xmlChar **attributes) { - - [(__bridge RSSAXHTMLParser *)context xmlStartElement:localname attributes:attributes]; -} - - -static void endElementSAX(void *context, const xmlChar *localname) { - [(__bridge RSSAXHTMLParser *)context xmlEndElement:localname]; -} - - -static void charactersFoundSAX(void *context, const xmlChar *ch, int len) { - [(__bridge RSSAXHTMLParser *)context xmlCharactersFound:ch length:(NSUInteger)len]; -} - - -static void endDocumentSAX(void *context) { - [(__bridge RSSAXHTMLParser *)context xmlEndDocument]; -} - - -static htmlSAXHandler saxHandlerStruct = { - nil, /* internalSubset */ - nil, /* isStandalone */ - nil, /* hasInternalSubset */ - nil, /* hasExternalSubset */ - nil, /* resolveEntity */ - nil, /* getEntity */ - nil, /* entityDecl */ - nil, /* notationDecl */ - nil, /* attributeDecl */ - nil, /* elementDecl */ - nil, /* unparsedEntityDecl */ - nil, /* setDocumentLocator */ - nil, /* startDocument */ - endDocumentSAX, /* endDocument */ - startElementSAX, /* startElement*/ - endElementSAX, /* endElement */ - nil, /* reference */ - charactersFoundSAX, /* characters */ - nil, /* ignorableWhitespace */ - nil, /* processingInstruction */ - nil, /* comment */ - nil, /* warning */ - nil, /* error */ - nil, /* fatalError //: unused error() get all the errors */ - nil, /* getParameterEntity */ - nil, /* cdataBlock */ - nil, /* externalSubset */ - XML_SAX2_MAGIC, - nil, - nil, /* startElementNs */ - nil, /* endElementNs */ - nil /* serror */ -}; - diff --git a/Frameworks/RSParser/Info.plist b/Frameworks/RSParser/Info.plist deleted file mode 100644 index b3516740c..000000000 --- a/Frameworks/RSParser/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSHumanReadableCopyright - Copyright © 2017 Ranchero Software, LLC. All rights reserved. - NSPrincipalClass - - - diff --git a/Frameworks/RSParser/JSON/JSONTypes.swift b/Frameworks/RSParser/JSON/JSONTypes.swift deleted file mode 100644 index fa3039a31..000000000 --- a/Frameworks/RSParser/JSON/JSONTypes.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// JSONDictionary.swift -// RSParser -// -// Created by Brent Simmons on 6/24/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -public typealias JSONDictionary = [String: Any] -public typealias JSONArray = [JSONDictionary] diff --git a/Frameworks/RSParser/JSON/JSONUtilities.swift b/Frameworks/RSParser/JSON/JSONUtilities.swift deleted file mode 100644 index 2319e0389..000000000 --- a/Frameworks/RSParser/JSON/JSONUtilities.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// JSONUtilities.swift -// RSParser -// -// Created by Brent Simmons on 12/10/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -public struct JSONUtilities { - - public static func object(with data: Data) -> Any? { - - return try? JSONSerialization.jsonObject(with: data) - } - - public static func dictionary(with data: Data) -> JSONDictionary? { - - return object(with: data) as? JSONDictionary - } - - public static func array(with data: Data) -> JSONArray? { - - return object(with: data) as? JSONArray - } -} diff --git a/Frameworks/RSParser/LICENSE b/Frameworks/RSParser/LICENSE deleted file mode 100644 index df7407c86..000000000 --- a/Frameworks/RSParser/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2017 Brent Simmons - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/Frameworks/RSParser/OPML/RSOPMLAttributes.h b/Frameworks/RSParser/OPML/RSOPMLAttributes.h deleted file mode 100755 index 688132fe5..000000000 --- a/Frameworks/RSParser/OPML/RSOPMLAttributes.h +++ /dev/null @@ -1,36 +0,0 @@ -// -// RSOPMLAttributes.h -// RSParser -// -// Created by Brent Simmons on 2/28/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -@import Foundation; - -// OPML allows for arbitrary attributes. -// These are the common attributes in OPML files used as RSS subscription lists. - -extern NSString *OPMLTextKey; //text -extern NSString *OPMLTitleKey; //title -extern NSString *OPMLDescriptionKey; //description -extern NSString *OPMLTypeKey; //type -extern NSString *OPMLVersionKey; //version -extern NSString *OPMLHMTLURLKey; //htmlUrl -extern NSString *OPMLXMLURLKey; //xmlUrl - - -@interface NSDictionary (RSOPMLAttributes) - -// A frequent error in OPML files is to mess up the capitalization, -// so these do a case-insensitive lookup. - -@property (nonatomic, readonly) NSString *opml_text; -@property (nonatomic, readonly) NSString *opml_title; -@property (nonatomic, readonly) NSString *opml_description; -@property (nonatomic, readonly) NSString *opml_type; -@property (nonatomic, readonly) NSString *opml_version; -@property (nonatomic, readonly) NSString *opml_htmlUrl; -@property (nonatomic, readonly) NSString *opml_xmlUrl; - -@end diff --git a/Frameworks/RSParser/OPML/RSOPMLAttributes.m b/Frameworks/RSParser/OPML/RSOPMLAttributes.m deleted file mode 100755 index 49bb91e8e..000000000 --- a/Frameworks/RSParser/OPML/RSOPMLAttributes.m +++ /dev/null @@ -1,66 +0,0 @@ -// -// RSOPMLAttributes.m -// RSParser -// -// Created by Brent Simmons on 2/28/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -#import -#import - - -NSString *OPMLTextKey = @"text"; -NSString *OPMLTitleKey = @"title"; -NSString *OPMLDescriptionKey = @"description"; -NSString *OPMLTypeKey = @"type"; -NSString *OPMLVersionKey = @"version"; -NSString *OPMLHMTLURLKey = @"htmlUrl"; -NSString *OPMLXMLURLKey = @"xmlUrl"; - - -@implementation NSDictionary (RSOPMLAttributes) - -- (NSString *)opml_text { - - return [self rsparser_objectForCaseInsensitiveKey:OPMLTextKey]; -} - - -- (NSString *)opml_title { - - return [self rsparser_objectForCaseInsensitiveKey:OPMLTitleKey]; -} - - -- (NSString *)opml_description { - - return [self rsparser_objectForCaseInsensitiveKey:OPMLDescriptionKey]; -} - - -- (NSString *)opml_type { - - return [self rsparser_objectForCaseInsensitiveKey:OPMLTypeKey]; -} - - -- (NSString *)opml_version { - - return [self rsparser_objectForCaseInsensitiveKey:OPMLVersionKey]; -} - - -- (NSString *)opml_htmlUrl { - - return [self rsparser_objectForCaseInsensitiveKey:OPMLHMTLURLKey]; -} - - -- (NSString *)opml_xmlUrl { - - return [self rsparser_objectForCaseInsensitiveKey:OPMLXMLURLKey]; -} - - -@end diff --git a/Frameworks/RSParser/OPML/RSOPMLDocument.h b/Frameworks/RSParser/OPML/RSOPMLDocument.h deleted file mode 100755 index 0d4398ab8..000000000 --- a/Frameworks/RSParser/OPML/RSOPMLDocument.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// RSOPMLDocument.h -// RSParser -// -// Created by Brent Simmons on 2/28/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -@import Foundation; -#import - - -@interface RSOPMLDocument : RSOPMLItem - -@property (nonatomic) NSString *title; - -@end diff --git a/Frameworks/RSParser/OPML/RSOPMLDocument.m b/Frameworks/RSParser/OPML/RSOPMLDocument.m deleted file mode 100755 index 02ac55956..000000000 --- a/Frameworks/RSParser/OPML/RSOPMLDocument.m +++ /dev/null @@ -1,13 +0,0 @@ -// -// RSOPMLDocument.m -// RSParser -// -// Created by Brent Simmons on 2/28/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -#import - -@implementation RSOPMLDocument - -@end diff --git a/Frameworks/RSParser/OPML/RSOPMLError.h b/Frameworks/RSParser/OPML/RSOPMLError.h deleted file mode 100755 index 276c62ed7..000000000 --- a/Frameworks/RSParser/OPML/RSOPMLError.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// RSOPMLError.h -// RSParser -// -// Created by Brent Simmons on 2/28/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -@import Foundation; - -extern NSString *RSOPMLErrorDomain; - - -typedef NS_ENUM(NSInteger, RSOPMLErrorCode) { - RSOPMLErrorCodeDataIsWrongFormat = 1024 -}; - - -NSError *RSOPMLWrongFormatError(NSString *fileName); diff --git a/Frameworks/RSParser/OPML/RSOPMLError.m b/Frameworks/RSParser/OPML/RSOPMLError.m deleted file mode 100755 index 93b477a41..000000000 --- a/Frameworks/RSParser/OPML/RSOPMLError.m +++ /dev/null @@ -1,22 +0,0 @@ -// -// RSOPMLError.m -// RSParser -// -// Created by Brent Simmons on 2/28/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -#import - -NSString *RSOPMLErrorDomain = @"com.ranchero.OPML"; - -NSError *RSOPMLWrongFormatError(NSString *fileName) { - - NSString *localizedDescriptionFormatString = NSLocalizedString(@"The file ‘%@’ can’t be parsed because it’s not an OPML file.", @"OPML wrong format"); - NSString *localizedDescription = [NSString stringWithFormat:localizedDescriptionFormatString, fileName]; - - NSString *localizedFailureString = NSLocalizedString(@"The file is not an OPML file.", @"OPML wrong format"); - NSDictionary *userInfo = @{NSLocalizedDescriptionKey: localizedDescription, NSLocalizedFailureReasonErrorKey: localizedFailureString}; - - return [[NSError alloc] initWithDomain:RSOPMLErrorDomain code:RSOPMLErrorCodeDataIsWrongFormat userInfo:userInfo]; -} diff --git a/Frameworks/RSParser/OPML/RSOPMLFeedSpecifier.h b/Frameworks/RSParser/OPML/RSOPMLFeedSpecifier.h deleted file mode 100755 index 8c4aea6b0..000000000 --- a/Frameworks/RSParser/OPML/RSOPMLFeedSpecifier.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// RSOPMLFeedSpecifier.h -// RSParser -// -// Created by Brent Simmons on 2/28/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -@import Foundation; - -NS_ASSUME_NONNULL_BEGIN - -@interface RSOPMLFeedSpecifier : NSObject - -- (instancetype)initWithTitle:(NSString * _Nullable)title feedDescription:(NSString * _Nullable)feedDescription homePageURL:(NSString * _Nullable)homePageURL feedURL:(NSString *)feedURL; - -@property (nonatomic, nullable, readonly) NSString *title; -@property (nonatomic, nullable, readonly) NSString *feedDescription; -@property (nonatomic, nullable, readonly) NSString *homePageURL; -@property (nonatomic, readonly) NSString *feedURL; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Frameworks/RSParser/OPML/RSOPMLFeedSpecifier.m b/Frameworks/RSParser/OPML/RSOPMLFeedSpecifier.m deleted file mode 100755 index 2147ff95c..000000000 --- a/Frameworks/RSParser/OPML/RSOPMLFeedSpecifier.m +++ /dev/null @@ -1,50 +0,0 @@ -// -// RSOPMLFeedSpecifier.m -// RSParser -// -// Created by Brent Simmons on 2/28/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -#import -#import - - -@implementation RSOPMLFeedSpecifier - -- (instancetype)initWithTitle:(NSString *)title feedDescription:(NSString *)feedDescription homePageURL:(NSString *)homePageURL feedURL:(NSString *)feedURL { - - NSParameterAssert(!RSParserStringIsEmpty(feedURL)); - - self = [super init]; - if (!self) { - return nil; - } - - if (RSParserStringIsEmpty(title)) { - _title = nil; - } - else { - _title = title; - } - - if (RSParserStringIsEmpty(feedDescription)) { - _feedDescription = nil; - } - else { - _feedDescription = feedDescription; - } - - if (RSParserStringIsEmpty(homePageURL)) { - _homePageURL = nil; - } - else { - _homePageURL = homePageURL; - } - - _feedURL = feedURL; - - return self; -} - -@end diff --git a/Frameworks/RSParser/OPML/RSOPMLItem.h b/Frameworks/RSParser/OPML/RSOPMLItem.h deleted file mode 100755 index 15afa48f2..000000000 --- a/Frameworks/RSParser/OPML/RSOPMLItem.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// RSOPMLItem.h -// RSParser -// -// Created by Brent Simmons on 2/28/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -@import Foundation; - -@class RSOPMLFeedSpecifier; - -NS_ASSUME_NONNULL_BEGIN - -@interface RSOPMLItem : NSObject - -@property (nonatomic, nullable) NSDictionary *attributes; -@property (nonatomic, nullable) NSArray *children; - -- (void)addChild:(RSOPMLItem *)child; - -@property (nonatomic, nullable, readonly) RSOPMLFeedSpecifier *feedSpecifier; - -@property (nonatomic, nullable, readonly) NSString *titleFromAttributes; -@property (nonatomic, readonly) BOOL isFolder; - -@end - -NS_ASSUME_NONNULL_END - diff --git a/Frameworks/RSParser/OPML/RSOPMLItem.m b/Frameworks/RSParser/OPML/RSOPMLItem.m deleted file mode 100755 index 3a9b8d64a..000000000 --- a/Frameworks/RSParser/OPML/RSOPMLItem.m +++ /dev/null @@ -1,86 +0,0 @@ -// -// RSOPMLItem.m -// RSParser -// -// Created by Brent Simmons on 2/28/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -#import -#import -#import -#import - - -@interface RSOPMLItem () - -@property (nonatomic) NSMutableArray *mutableChildren; - -@end - - -@implementation RSOPMLItem - -@synthesize children = _children; -@synthesize feedSpecifier = _feedSpecifier; - - -- (NSArray *)children { - - return [self.mutableChildren copy]; -} - - -- (void)setChildren:(NSArray *)children { - - _children = children; - self.mutableChildren = [_children mutableCopy]; -} - - -- (void)addChild:(RSOPMLItem *)child { - - if (!self.mutableChildren) { - self.mutableChildren = [NSMutableArray new]; - } - - [self.mutableChildren addObject:child]; -} - - -- (RSOPMLFeedSpecifier *)feedSpecifier { - - if (_feedSpecifier) { - return _feedSpecifier; - } - - NSString *feedURL = self.attributes.opml_xmlUrl; - if (RSParserObjectIsEmpty(feedURL)) { - return nil; - } - - _feedSpecifier = [[RSOPMLFeedSpecifier alloc] initWithTitle:self.attributes.opml_title feedDescription:self.attributes.opml_description homePageURL:self.attributes.opml_htmlUrl feedURL:feedURL]; - - return _feedSpecifier; -} - -- (NSString *)titleFromAttributes { - - NSString *title = self.attributes.opml_title; - if (title) { - return title; - } - title = self.attributes.opml_text; - if (title) { - return title; - } - - return nil; -} - -- (BOOL)isFolder { - - return self.mutableChildren.count > 0; -} - -@end diff --git a/Frameworks/RSParser/OPML/RSOPMLParser.h b/Frameworks/RSParser/OPML/RSOPMLParser.h deleted file mode 100755 index 8db594b03..000000000 --- a/Frameworks/RSParser/OPML/RSOPMLParser.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// RSOPMLParser.h -// RSParser -// -// Created by Brent Simmons on 7/12/15. -// Copyright © 2015 Ranchero Software, LLC. All rights reserved. -// - -@import Foundation; - - -@class ParserData; -@class RSOPMLDocument; - -typedef void (^OPMLParserCallback)(RSOPMLDocument *opmlDocument, NSError *error); - -// Parses on background thread; calls back on main thread. -void RSParseOPML(ParserData *parserData, OPMLParserCallback callback); - - -@interface RSOPMLParser: NSObject - -+ (RSOPMLDocument *)parseOPMLWithParserData:(ParserData *)parserData error:(NSError **)error; - -@end - diff --git a/Frameworks/RSParser/OPML/RSOPMLParser.m b/Frameworks/RSParser/OPML/RSOPMLParser.m deleted file mode 100755 index d23f9e61b..000000000 --- a/Frameworks/RSParser/OPML/RSOPMLParser.m +++ /dev/null @@ -1,301 +0,0 @@ -// -// RSOPMLParser.m -// RSParser -// -// Created by Brent Simmons on 7/12/15. -// Copyright © 2015 Ranchero Software, LLC. All rights reserved. -// - -#import "RSOPMLParser.h" -#import -#import -#import -#import -#import -#import -#import - - -@interface RSOPMLParser () - -@property (nonatomic, readwrite) RSOPMLDocument *OPMLDocument; -@property (nonatomic, readwrite) NSError *error; -@property (nonatomic) NSMutableArray *itemStack; - -@end - -void RSParseOPML(ParserData *parserData, OPMLParserCallback callback) { - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - - @autoreleasepool { - NSError *error = nil; - RSOPMLDocument *opmlDocument = [RSOPMLParser parseOPMLWithParserData:parserData error:&error]; - - dispatch_async(dispatch_get_main_queue(), ^{ - callback(opmlDocument, error); - }); - } - }); -} - -@implementation RSOPMLParser - -#pragma mark - Class Methods - -+ (RSOPMLDocument *)parseOPMLWithParserData:(ParserData *)parserData error:(NSError **)error { - - RSOPMLParser *parser = [[RSOPMLParser alloc] initWithParserData:parserData]; - - RSOPMLDocument *document = parser.OPMLDocument; - if (parser.error && error) { - *error = parser.error; - return nil; - } - return document; -} - -#pragma mark - Init - -- (instancetype)initWithParserData:(ParserData *)parserData { - - self = [super init]; - if (!self) { - return nil; - } - - [self parse:parserData]; - - return self; -} - - -#pragma mark - Private - -- (void)parse:(ParserData *)parserData { - - @autoreleasepool { - - if (![self canParseData:parserData.data]) { - - NSString *filename = nil; - NSURL *url = [NSURL URLWithString:parserData.url]; - if (url && url.isFileURL) { - filename = url.path.lastPathComponent; - } - if ([parserData.url hasPrefix:@"http"]) { - filename = parserData.url; - } - if (!filename) { - filename = parserData.url; - } - self.error = RSOPMLWrongFormatError(filename); - return; - } - - RSSAXParser *parser = [[RSSAXParser alloc] initWithDelegate:self]; - - self.itemStack = [NSMutableArray new]; - self.OPMLDocument = [RSOPMLDocument new]; - [self pushItem:self.OPMLDocument]; - - [parser parseData:parserData.data]; - [parser finishParsing]; - } -} - -- (BOOL)canParseData:(NSData *)d { - - // Check for 0, nil); - - /*If itemStack is empty, bad things are happening. - But we still shouldn't crash in production.*/ - - if (self.itemStack.count > 0) { - [self.itemStack removeLastObject]; - } -} - - -- (RSOPMLItem *)currentItem { - - return self.itemStack.lastObject; -} - - -#pragma mark - RSSAXParserDelegate - -static const char *kOutline = "outline"; -static const char kOutlineLength = 8; - -- (void)saxParser:(RSSAXParser *)SAXParser XMLStartElement:(const xmlChar *)localName prefix:(const xmlChar *)prefix uri:(const xmlChar *)uri numberOfNamespaces:(NSInteger)numberOfNamespaces namespaces:(const xmlChar **)namespaces numberOfAttributes:(NSInteger)numberOfAttributes numberDefaulted:(int)numberDefaulted attributes:(const xmlChar **)attributes { - - if (!RSSAXEqualTags(localName, kOutline, kOutlineLength)) { - return; - } - - RSOPMLItem *item = [RSOPMLItem new]; - item.attributes = [SAXParser attributesDictionary:attributes numberOfAttributes:numberOfAttributes]; - - [[self currentItem] addChild:item]; - [self pushItem:item]; -} - - -- (void)saxParser:(RSSAXParser *)SAXParser XMLEndElement:(const xmlChar *)localName prefix:(const xmlChar *)prefix uri:(const xmlChar *)uri { - - if (RSSAXEqualTags(localName, kOutline, kOutlineLength)) { - [self popItem]; - } -} - - -static const char *kText = "text"; -static const NSInteger kTextLength = 5; - -static const char *kTitle = "title"; -static const NSInteger kTitleLength = 6; - -static const char *kDescription = "description"; -static const NSInteger kDescriptionLength = 12; - -static const char *kType = "type"; -static const NSInteger kTypeLength = 5; - -static const char *kVersion = "version"; -static const NSInteger kVersionLength = 8; - -static const char *kHTMLURL = "htmlUrl"; -static const NSInteger kHTMLURLLength = 8; - -static const char *kXMLURL = "xmlUrl"; -static const NSInteger kXMLURLLength = 7; - -- (NSString *)saxParser:(RSSAXParser *)SAXParser internedStringForName:(const xmlChar *)name prefix:(const xmlChar *)prefix { - - if (prefix) { - return nil; - } - - size_t nameLength = strlen((const char *)name); - - if (nameLength == kTextLength - 1) { - if (RSSAXEqualTags(name, kText, kTextLength)) { - return OPMLTextKey; - } - if (RSSAXEqualTags(name, kType, kTypeLength)) { - return OPMLTypeKey; - } - } - - else if (nameLength == kTitleLength - 1) { - if (RSSAXEqualTags(name, kTitle, kTitleLength)) { - return OPMLTitleKey; - } - } - - else if (nameLength == kXMLURLLength - 1) { - if (RSSAXEqualTags(name, kXMLURL, kXMLURLLength)) { - return OPMLXMLURLKey; - } - } - - else if (nameLength == kVersionLength - 1) { - if (RSSAXEqualTags(name, kVersion, kVersionLength)) { - return OPMLVersionKey; - } - if (RSSAXEqualTags(name, kHTMLURL, kHTMLURLLength)) { - return OPMLHMTLURLKey; - } - } - - else if (nameLength == kDescriptionLength - 1) { - if (RSSAXEqualTags(name, kDescription, kDescriptionLength)) { - return OPMLDescriptionKey; - } - } - - return nil; -} - - -static const char *kRSSUppercase = "RSS"; -static const char *kRSSLowercase = "rss"; -static const NSUInteger kRSSLength = 3; -static NSString *RSSUppercaseValue = @"RSS"; -static NSString *RSSLowercaseValue = @"rss"; -static NSString *emptyString = @""; - -static BOOL equalBytes(const void *bytes1, const void *bytes2, NSUInteger length) { - - return memcmp(bytes1, bytes2, length) == 0; -} - -- (NSString *)saxParser:(RSSAXParser *)SAXParser internedStringForValue:(const void *)bytes length:(NSUInteger)length { - - - if (length < 1) { - return emptyString; - } - - if (length == kRSSLength) { - - if (equalBytes(bytes, kRSSUppercase, kRSSLength)) { - return RSSUppercaseValue; - } - else if (equalBytes(bytes, kRSSLowercase, kRSSLength)) { - return RSSLowercaseValue; - } - - } - - return nil; -} - - -@end diff --git a/Frameworks/RSParser/ParserData.h b/Frameworks/RSParser/ParserData.h deleted file mode 100644 index 30517d98d..000000000 --- a/Frameworks/RSParser/ParserData.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// ParserData.h -// RSParser -// -// Created by Brent Simmons on 10/4/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -@import Foundation; - -NS_ASSUME_NONNULL_BEGIN - -@interface ParserData : NSObject - -@property (nonatomic, readonly) NSString *url; -@property (nonatomic, readonly) NSData *data; - -- (instancetype)initWithURL:(NSString *)url data:(NSData *)data; - -@end - -NS_ASSUME_NONNULL_END - diff --git a/Frameworks/RSParser/ParserData.m b/Frameworks/RSParser/ParserData.m deleted file mode 100644 index 68c5f0356..000000000 --- a/Frameworks/RSParser/ParserData.m +++ /dev/null @@ -1,26 +0,0 @@ -// -// ParserData.m -// RSParser -// -// Created by Brent Simmons on 10/4/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -#import "ParserData.h" - -@implementation ParserData - -- (instancetype)initWithURL:(NSString *)url data:(NSData *)data { - - self = [super init]; - if (!self) { - return nil; - } - - _url = url; - _data = data; - - return self; -} - -@end diff --git a/Frameworks/RSParser/README.md b/Frameworks/RSParser/README.md deleted file mode 100644 index cb477deea..000000000 --- a/Frameworks/RSParser/README.md +++ /dev/null @@ -1,73 +0,0 @@ -# RSParser - -This framework was developed for [Evergreen](https://github.com/brentsimmons/Evergreen) and is made available here for developers who just need the parsing code. It has no depencies that aren’t provided by the system. - -## What’s inside - -This framework includes parsers for: - -* [RSS](http://cyber.harvard.edu/rss/rss.html), [Atom](https://tools.ietf.org/html/rfc4287), [JSON Feed](https://jsonfeed.org/), and [RSS-in-JSON](https://github.com/scripting/Scripting-News/blob/master/rss-in-json/README.md) -* [OPML](http://dev.opml.org/) -* Internet dates -* HTML metadata and links -* HTML entities - -It also includes Objective-C wrappers for libXML2’s XML SAX and HTML SAX parsers. You can write your own parsers on top of these. - -This framework builds for macOS. It *could* be made to build for iOS also, but I haven’t gotten around to it yet. - -## How to parse feeds - -To get the type of a feed, even with partial data, call `FeedParser.feedType(parserData)`, which will return a `FeedType`. - -To parse a feed, call `FeedParser.parse(parserData)`, which will return a [ParsedFeed](Feeds/ParsedFeed.swift). Also see related structs: `ParsedAuthor`, `ParsedItem`, `ParsedAttachment`, and `ParsedHub`. - -You do *not* need to know the type of feed when calling `FeedParser.parse` — it will figure it out and use the correct concrete parser. - -However, if you do want to use a concrete parser directly, see [RSSInJSONParser](Feeds/JSON/RSSInJSONParser.swift), [JSONFeedParser](Feeds/JSON/JSONFeedParser.swift), [RSSParser](Feeds/XML/RSSParser.swift), and [AtomParser](Feeds/XML/AtomParser.swift). - -(Note: if you want to write a feed reader app, please do! You have my blessing and encouragement. Let me know when it’s shipping so I can check it out.) - -## How to parse OPML - -Call `+[RSOPMLParser parseOPMLWithParserData:error:]`, which returns an `RSOPMLDocument`. See related objects: `RSOPMLItem`, `RSOPMLAttributes`, `RSOPMLFeedSpecifier`, and `RSOPMLError`. - -## How to parse dates - -Call `RSDateWithString` or `RSDateWithBytes` (see `RSDateParser`). These handle the common internet date formats. You don’t need to know which format. - -## How to parse HTML - -To get an array of ` - -// Dates - -#import - -// OPML - -#import -#import -#import -#import -#import -#import - -// For writing your own XML parser. - -#import - -// You should use FeedParser (Swift) instead of these two specific parsers -// and the objects they create. -// But they’re available if you want them. - -#import -#import -#import -#import -#import -#import - -// HTML - -#import -#import -#import -#import // For writing your own HTML parser. -#import - -// Utilities - -#import -#import diff --git a/Frameworks/RSParser/RSParser.xcodeproj/project.pbxproj b/Frameworks/RSParser/RSParser.xcodeproj/project.pbxproj deleted file mode 100644 index ab1ffab79..000000000 --- a/Frameworks/RSParser/RSParser.xcodeproj/project.pbxproj +++ /dev/null @@ -1,791 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 48; - objects = { - -/* Begin PBXBuildFile section */ - 8401FF811FE862E70080F13F /* RSParsedEnclosure.h in Headers */ = {isa = PBXBuildFile; fileRef = 8401FF7F1FE862E70080F13F /* RSParsedEnclosure.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8401FF821FE862E70080F13F /* RSParsedEnclosure.m in Sources */ = {isa = PBXBuildFile; fileRef = 8401FF801FE862E70080F13F /* RSParsedEnclosure.m */; }; - 8401FF841FE87C2F0080F13F /* theomnishow.rss in Resources */ = {isa = PBXBuildFile; fileRef = 8401FF831FE87C2E0080F13F /* theomnishow.rss */; }; - 840FDCB21F01FED00041F61B /* RSSParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840FDCB11F01FED00041F61B /* RSSParserTests.swift */; }; - 840FDCB41F0217410041F61B /* JSONFeedParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840FDCB31F0217410041F61B /* JSONFeedParserTests.swift */; }; - 840FDCB61F0217D20041F61B /* AtomParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840FDCB51F0217D20041F61B /* AtomParserTests.swift */; }; - 840FDCB81F0218670041F61B /* DaringFireball.atom in Resources */ = {isa = PBXBuildFile; fileRef = 840FDCB71F0218670041F61B /* DaringFireball.atom */; }; - 840FDCBA1F02186D0041F61B /* DaringFireball.json in Resources */ = {isa = PBXBuildFile; fileRef = 840FDCB91F02186D0041F61B /* DaringFireball.json */; }; - 84245C881FDDDC6D0074AFBB /* JSONUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84245C871FDDDC6D0074AFBB /* JSONUtilities.swift */; }; - 84285AA81F005D53002E8708 /* RSSParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84285AA71F005D53002E8708 /* RSSParser.swift */; }; - 84285AAA1F006456002E8708 /* RSParsedFeedTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84285AA91F006456002E8708 /* RSParsedFeedTransformer.swift */; }; - 84285AAC1F006754002E8708 /* AtomParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84285AAB1F006754002E8708 /* AtomParser.swift */; }; - 84285AAF1F006BC0002E8708 /* libxml2.2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 84285AAE1F006BC0002E8708 /* libxml2.2.tbd */; }; - 84285AB21F00702E002E8708 /* RSOPMLError.h in Headers */ = {isa = PBXBuildFile; fileRef = 84285AB01F00702E002E8708 /* RSOPMLError.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 84285AB31F00702E002E8708 /* RSOPMLError.m in Sources */ = {isa = PBXBuildFile; fileRef = 84285AB11F00702E002E8708 /* RSOPMLError.m */; }; - 842A42011FB77A5A00EA22E4 /* dcrainmaker.xml in Resources */ = {isa = PBXBuildFile; fileRef = 842A42001FB77A5A00EA22E4 /* dcrainmaker.xml */; }; - 84469CEF1EFA3000004A6B28 /* RSOPMLAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 84469CE51EFA3000004A6B28 /* RSOPMLAttributes.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 84469CF01EFA3000004A6B28 /* RSOPMLAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = 84469CE61EFA3000004A6B28 /* RSOPMLAttributes.m */; }; - 84469CF11EFA3000004A6B28 /* RSOPMLDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = 84469CE71EFA3000004A6B28 /* RSOPMLDocument.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 84469CF21EFA3000004A6B28 /* RSOPMLDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = 84469CE81EFA3000004A6B28 /* RSOPMLDocument.m */; }; - 84469CF31EFA3000004A6B28 /* RSOPMLFeedSpecifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 84469CE91EFA3000004A6B28 /* RSOPMLFeedSpecifier.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 84469CF41EFA3000004A6B28 /* RSOPMLFeedSpecifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 84469CEA1EFA3000004A6B28 /* RSOPMLFeedSpecifier.m */; }; - 84469CF51EFA3000004A6B28 /* RSOPMLItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 84469CEB1EFA3000004A6B28 /* RSOPMLItem.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 84469CF61EFA3000004A6B28 /* RSOPMLItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 84469CEC1EFA3000004A6B28 /* RSOPMLItem.m */; }; - 84469CF71EFA3000004A6B28 /* RSOPMLParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 84469CED1EFA3000004A6B28 /* RSOPMLParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 84469CF81EFA3000004A6B28 /* RSOPMLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 84469CEE1EFA3000004A6B28 /* RSOPMLParser.m */; }; - 84469CFC1EFA3069004A6B28 /* RSSAXParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 84469CFA1EFA3069004A6B28 /* RSSAXParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 84469CFD1EFA3069004A6B28 /* RSSAXParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 84469CFB1EFA3069004A6B28 /* RSSAXParser.m */; }; - 84469D071EFA307E004A6B28 /* RSHTMLLinkParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 84469CFF1EFA307E004A6B28 /* RSHTMLLinkParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 84469D081EFA307E004A6B28 /* RSHTMLLinkParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 84469D001EFA307E004A6B28 /* RSHTMLLinkParser.m */; }; - 84469D091EFA307E004A6B28 /* RSHTMLMetadata.h in Headers */ = {isa = PBXBuildFile; fileRef = 84469D011EFA307E004A6B28 /* RSHTMLMetadata.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 84469D0A1EFA307E004A6B28 /* RSHTMLMetadata.m in Sources */ = {isa = PBXBuildFile; fileRef = 84469D021EFA307E004A6B28 /* RSHTMLMetadata.m */; }; - 84469D0B1EFA307E004A6B28 /* RSHTMLMetadataParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 84469D031EFA307E004A6B28 /* RSHTMLMetadataParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 84469D0C1EFA307E004A6B28 /* RSHTMLMetadataParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 84469D041EFA307E004A6B28 /* RSHTMLMetadataParser.m */; }; - 84469D0D1EFA307E004A6B28 /* RSSAXHTMLParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 84469D051EFA307E004A6B28 /* RSSAXHTMLParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 84469D0E1EFA307E004A6B28 /* RSSAXHTMLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 84469D061EFA307E004A6B28 /* RSSAXHTMLParser.m */; }; - 84469D161EFA30A2004A6B28 /* NSString+RSParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 84469D101EFA30A2004A6B28 /* NSString+RSParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 84469D171EFA30A2004A6B28 /* NSString+RSParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 84469D111EFA30A2004A6B28 /* NSString+RSParser.m */; }; - 84469D181EFA30A2004A6B28 /* RSDateParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 84469D121EFA30A2004A6B28 /* RSDateParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 84469D191EFA30A2004A6B28 /* RSDateParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 84469D131EFA30A2004A6B28 /* RSDateParser.m */; }; - 84469D1A1EFA30A2004A6B28 /* RSParserInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 84469D141EFA30A2004A6B28 /* RSParserInternal.h */; }; - 84469D1B1EFA30A2004A6B28 /* RSParserInternal.m in Sources */ = {isa = PBXBuildFile; fileRef = 84469D151EFA30A2004A6B28 /* RSParserInternal.m */; }; - 84469D271EFA3134004A6B28 /* RSAtomParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 84469D1D1EFA3134004A6B28 /* RSAtomParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 84469D281EFA3134004A6B28 /* RSAtomParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 84469D1E1EFA3134004A6B28 /* RSAtomParser.m */; }; - 84469D2B1EFA3134004A6B28 /* RSParsedArticle.h in Headers */ = {isa = PBXBuildFile; fileRef = 84469D211EFA3134004A6B28 /* RSParsedArticle.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 84469D2C1EFA3134004A6B28 /* RSParsedArticle.m in Sources */ = {isa = PBXBuildFile; fileRef = 84469D221EFA3134004A6B28 /* RSParsedArticle.m */; }; - 84469D2D1EFA3134004A6B28 /* RSParsedFeed.h in Headers */ = {isa = PBXBuildFile; fileRef = 84469D231EFA3134004A6B28 /* RSParsedFeed.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 84469D2E1EFA3134004A6B28 /* RSParsedFeed.m in Sources */ = {isa = PBXBuildFile; fileRef = 84469D241EFA3134004A6B28 /* RSParsedFeed.m */; }; - 84469D2F1EFA3134004A6B28 /* RSRSSParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 84469D251EFA3134004A6B28 /* RSRSSParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 84469D301EFA3134004A6B28 /* RSRSSParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 84469D261EFA3134004A6B28 /* RSRSSParser.m */; }; - 84469D321EFA31CF004A6B28 /* FeedParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84469D311EFA31CF004A6B28 /* FeedParser.swift */; }; - 84469D351EFF1190004A6B28 /* NSData+RSParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 84469D331EFF1190004A6B28 /* NSData+RSParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 84469D361EFF1190004A6B28 /* NSData+RSParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 84469D341EFF1190004A6B28 /* NSData+RSParser.m */; }; - 84469D381EFF2645004A6B28 /* RSSInJSONParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84469D371EFF2645004A6B28 /* RSSInJSONParser.swift */; }; - 84469D401EFF29A9004A6B28 /* FeedParserError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84469D3F1EFF29A9004A6B28 /* FeedParserError.swift */; }; - 84469D421EFF2B2D004A6B28 /* JSONTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84469D411EFF2B2D004A6B28 /* JSONTypes.swift */; }; - 84469D441F002CEF004A6B28 /* JSONFeedParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84469D431F002CEF004A6B28 /* JSONFeedParser.swift */; }; - 844B5B3E1FE9A13C00C7C76A /* 4fsodonline.atom in Resources */ = {isa = PBXBuildFile; fileRef = 844B5B3D1FE9A13B00C7C76A /* 4fsodonline.atom */; }; - 844B5B401FE9A45200C7C76A /* expertopinionent.atom in Resources */ = {isa = PBXBuildFile; fileRef = 844B5B3F1FE9A45200C7C76A /* expertopinionent.atom */; }; - 844B5B441FE9AFDB00C7C76A /* RSParsedAuthor.h in Headers */ = {isa = PBXBuildFile; fileRef = 844B5B411FE9AF2700C7C76A /* RSParsedAuthor.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 844B5B451FE9AFE000C7C76A /* RSParsedAuthor.m in Sources */ = {isa = PBXBuildFile; fileRef = 844B5B421FE9AF2700C7C76A /* RSParsedAuthor.m */; }; - 845213251FCB3C76003B6E93 /* coco.html in Resources */ = {isa = PBXBuildFile; fileRef = 845213241FCB3C75003B6E93 /* coco.html */; }; - 845213281FCB4042003B6E93 /* RSHTMLTag.h in Headers */ = {isa = PBXBuildFile; fileRef = 845213261FCB4042003B6E93 /* RSHTMLTag.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 845213291FCB4042003B6E93 /* RSHTMLTag.m in Sources */ = {isa = PBXBuildFile; fileRef = 845213271FCB4042003B6E93 /* RSHTMLTag.m */; }; - 84566D941FD0ABFB00103322 /* allthis.json in Resources */ = {isa = PBXBuildFile; fileRef = 84566D931FD0ABFB00103322 /* allthis.json */; }; - 84566D961FD1FC1800103322 /* allthis-partial.json in Resources */ = {isa = PBXBuildFile; fileRef = 84566D951FD1FC1800103322 /* allthis-partial.json */; }; - 84628AAD1FCA10AE00566A9B /* allthis.atom in Resources */ = {isa = PBXBuildFile; fileRef = 84628AAC1FCA10AE00566A9B /* allthis.atom */; }; - 848674D21FCE7BF600802D1F /* macworld.rss in Resources */ = {isa = PBXBuildFile; fileRef = 848674D11FCE7BF500802D1F /* macworld.rss */; }; - 849A03D01F0081EA00122600 /* DaringFireball.html in Resources */ = {isa = PBXBuildFile; fileRef = 849A03C51F0081EA00122600 /* DaringFireball.html */; }; - 849A03D11F0081EA00122600 /* DaringFireball.rss in Resources */ = {isa = PBXBuildFile; fileRef = 849A03C61F0081EA00122600 /* DaringFireball.rss */; }; - 849A03D21F0081EA00122600 /* EMarley.rss in Resources */ = {isa = PBXBuildFile; fileRef = 849A03C71F0081EA00122600 /* EMarley.rss */; }; - 849A03D31F0081EA00122600 /* furbo.html in Resources */ = {isa = PBXBuildFile; fileRef = 849A03C81F0081EA00122600 /* furbo.html */; }; - 849A03D41F0081EA00122600 /* inessential.html in Resources */ = {isa = PBXBuildFile; fileRef = 849A03C91F0081EA00122600 /* inessential.html */; }; - 849A03D51F0081EA00122600 /* KatieFloyd.rss in Resources */ = {isa = PBXBuildFile; fileRef = 849A03CA1F0081EA00122600 /* KatieFloyd.rss */; }; - 849A03D61F0081EA00122600 /* manton.rss in Resources */ = {isa = PBXBuildFile; fileRef = 849A03CB1F0081EA00122600 /* manton.rss */; }; - 849A03D71F0081EA00122600 /* OneFootTsunami.atom in Resources */ = {isa = PBXBuildFile; fileRef = 849A03CC1F0081EA00122600 /* OneFootTsunami.atom */; }; - 849A03D81F0081EA00122600 /* scriptingNews.rss in Resources */ = {isa = PBXBuildFile; fileRef = 849A03CD1F0081EA00122600 /* scriptingNews.rss */; }; - 849A03D91F0081EA00122600 /* sixcolors.html in Resources */ = {isa = PBXBuildFile; fileRef = 849A03CE1F0081EA00122600 /* sixcolors.html */; }; - 849A03DA1F0081EA00122600 /* Subs.opml in Resources */ = {isa = PBXBuildFile; fileRef = 849A03CF1F0081EA00122600 /* Subs.opml */; }; - 849A03DE1F00837A00122600 /* FeedParserTypeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A03DD1F00837A00122600 /* FeedParserTypeTests.swift */; }; - 849A03E01F0088F600122600 /* OPMLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A03DF1F0088F600122600 /* OPMLTests.swift */; }; - 849A03E21F00902C00122600 /* RSDateParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 849A03E11F00902C00122600 /* RSDateParserTests.m */; }; - 849A03E41F00912100122600 /* HTMLMetadataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A03E31F00912100122600 /* HTMLMetadataTests.swift */; }; - 849A03E61F00952400122600 /* HTMLLinkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A03E51F00952400122600 /* HTMLLinkTests.swift */; }; - 849A03E81F01F88600122600 /* ScriptingNews.json in Resources */ = {isa = PBXBuildFile; fileRef = 849A03E71F01F88600122600 /* ScriptingNews.json */; }; - 849A03EA1F01F92B00122600 /* inessential.json in Resources */ = {isa = PBXBuildFile; fileRef = 849A03E91F01F92B00122600 /* inessential.json */; }; - 849A03EC1F01FCDC00122600 /* RSSInJSONParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A03EB1F01FCDC00122600 /* RSSInJSONParserTests.swift */; }; - 84B19A771FDA438300458981 /* natasha.xml in Resources */ = {isa = PBXBuildFile; fileRef = 84B19A761FDA438300458981 /* natasha.xml */; }; - 84CF85BB2035455B0096F368 /* pxlnv.json in Resources */ = {isa = PBXBuildFile; fileRef = 84CF85BA2035455B0096F368 /* pxlnv.json */; }; - 84D81BDC1EFA28E700652332 /* RSParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D81BDA1EFA28E700652332 /* RSParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 84D81BDE1EFA2B7D00652332 /* ParsedFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D81BDD1EFA2B7D00652332 /* ParsedFeed.swift */; }; - 84D81BE01EFA2BAE00652332 /* FeedType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D81BDF1EFA2BAE00652332 /* FeedType.swift */; }; - 84D81BE21EFA2D0900652332 /* ParsedAuthor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D81BE11EFA2D0900652332 /* ParsedAuthor.swift */; }; - 84D81BE41EFA2D3D00652332 /* ParsedItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D81BE31EFA2D3D00652332 /* ParsedItem.swift */; }; - 84D81BE61EFA2DFB00652332 /* ParsedAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D81BE51EFA2DFB00652332 /* ParsedAttachment.swift */; }; - 84D81BE81EFA2E6700652332 /* ParsedHub.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D81BE71EFA2E6700652332 /* ParsedHub.swift */; }; - 84DA2E21200415D500A4D03B /* curt.json in Resources */ = {isa = PBXBuildFile; fileRef = 84DA2E20200415D500A4D03B /* curt.json */; }; - 84DCCC661FF80E0100D2DDF1 /* EntityDecodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DCCC651FF80E0100D2DDF1 /* EntityDecodingTests.swift */; }; - 84E7E69F1F85780D0046719D /* ParserData.h in Headers */ = {isa = PBXBuildFile; fileRef = 84E7E69D1F85780D0046719D /* ParserData.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 84E7E6A01F85780D0046719D /* ParserData.m in Sources */ = {isa = PBXBuildFile; fileRef = 84E7E69E1F85780D0046719D /* ParserData.m */; }; - 84FF5F8E1EFA285800C15A01 /* RSParser.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FF5F841EFA285800C15A01 /* RSParser.framework */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 84FF5F8F1EFA285800C15A01 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 84FF5F7B1EFA285800C15A01 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 84FF5F831EFA285800C15A01; - remoteInfo = RSParser; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 8401FF7F1FE862E70080F13F /* RSParsedEnclosure.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RSParsedEnclosure.h; sourceTree = ""; }; - 8401FF801FE862E70080F13F /* RSParsedEnclosure.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RSParsedEnclosure.m; sourceTree = ""; }; - 8401FF831FE87C2E0080F13F /* theomnishow.rss */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = theomnishow.rss; sourceTree = ""; }; - 840FDCB11F01FED00041F61B /* RSSParserTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSSParserTests.swift; sourceTree = ""; }; - 840FDCB31F0217410041F61B /* JSONFeedParserTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONFeedParserTests.swift; sourceTree = ""; }; - 840FDCB51F0217D20041F61B /* AtomParserTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AtomParserTests.swift; sourceTree = ""; }; - 840FDCB71F0218670041F61B /* DaringFireball.atom */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = DaringFireball.atom; sourceTree = ""; }; - 840FDCB91F02186D0041F61B /* DaringFireball.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = DaringFireball.json; sourceTree = ""; }; - 84245C871FDDDC6D0074AFBB /* JSONUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONUtilities.swift; sourceTree = ""; }; - 84285AA71F005D53002E8708 /* RSSParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSSParser.swift; sourceTree = ""; }; - 84285AA91F006456002E8708 /* RSParsedFeedTransformer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSParsedFeedTransformer.swift; sourceTree = ""; }; - 84285AAB1F006754002E8708 /* AtomParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AtomParser.swift; sourceTree = ""; }; - 84285AAE1F006BC0002E8708 /* libxml2.2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.2.tbd; path = usr/lib/libxml2.2.tbd; sourceTree = SDKROOT; }; - 84285AB01F00702E002E8708 /* RSOPMLError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSOPMLError.h; sourceTree = ""; }; - 84285AB11F00702E002E8708 /* RSOPMLError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSOPMLError.m; sourceTree = ""; }; - 84285AB41F007255002E8708 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - 842A42001FB77A5A00EA22E4 /* dcrainmaker.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = dcrainmaker.xml; sourceTree = ""; }; - 84469CE51EFA3000004A6B28 /* RSOPMLAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSOPMLAttributes.h; sourceTree = ""; }; - 84469CE61EFA3000004A6B28 /* RSOPMLAttributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSOPMLAttributes.m; sourceTree = ""; }; - 84469CE71EFA3000004A6B28 /* RSOPMLDocument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSOPMLDocument.h; sourceTree = ""; }; - 84469CE81EFA3000004A6B28 /* RSOPMLDocument.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSOPMLDocument.m; sourceTree = ""; }; - 84469CE91EFA3000004A6B28 /* RSOPMLFeedSpecifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSOPMLFeedSpecifier.h; sourceTree = ""; }; - 84469CEA1EFA3000004A6B28 /* RSOPMLFeedSpecifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSOPMLFeedSpecifier.m; sourceTree = ""; }; - 84469CEB1EFA3000004A6B28 /* RSOPMLItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSOPMLItem.h; sourceTree = ""; }; - 84469CEC1EFA3000004A6B28 /* RSOPMLItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSOPMLItem.m; sourceTree = ""; }; - 84469CED1EFA3000004A6B28 /* RSOPMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSOPMLParser.h; sourceTree = ""; }; - 84469CEE1EFA3000004A6B28 /* RSOPMLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSOPMLParser.m; sourceTree = ""; }; - 84469CFA1EFA3069004A6B28 /* RSSAXParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSSAXParser.h; sourceTree = ""; }; - 84469CFB1EFA3069004A6B28 /* RSSAXParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSSAXParser.m; sourceTree = ""; }; - 84469CFF1EFA307E004A6B28 /* RSHTMLLinkParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSHTMLLinkParser.h; sourceTree = ""; }; - 84469D001EFA307E004A6B28 /* RSHTMLLinkParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSHTMLLinkParser.m; sourceTree = ""; }; - 84469D011EFA307E004A6B28 /* RSHTMLMetadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSHTMLMetadata.h; sourceTree = ""; }; - 84469D021EFA307E004A6B28 /* RSHTMLMetadata.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSHTMLMetadata.m; sourceTree = ""; }; - 84469D031EFA307E004A6B28 /* RSHTMLMetadataParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSHTMLMetadataParser.h; sourceTree = ""; }; - 84469D041EFA307E004A6B28 /* RSHTMLMetadataParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSHTMLMetadataParser.m; sourceTree = ""; }; - 84469D051EFA307E004A6B28 /* RSSAXHTMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSSAXHTMLParser.h; sourceTree = ""; }; - 84469D061EFA307E004A6B28 /* RSSAXHTMLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSSAXHTMLParser.m; sourceTree = ""; }; - 84469D101EFA30A2004A6B28 /* NSString+RSParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+RSParser.h"; sourceTree = ""; }; - 84469D111EFA30A2004A6B28 /* NSString+RSParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+RSParser.m"; sourceTree = ""; }; - 84469D121EFA30A2004A6B28 /* RSDateParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RSDateParser.h; path = Utilities/RSDateParser.h; sourceTree = ""; }; - 84469D131EFA30A2004A6B28 /* RSDateParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RSDateParser.m; path = Utilities/RSDateParser.m; sourceTree = ""; }; - 84469D141EFA30A2004A6B28 /* RSParserInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSParserInternal.h; sourceTree = ""; }; - 84469D151EFA30A2004A6B28 /* RSParserInternal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSParserInternal.m; sourceTree = ""; }; - 84469D1D1EFA3134004A6B28 /* RSAtomParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSAtomParser.h; sourceTree = ""; }; - 84469D1E1EFA3134004A6B28 /* RSAtomParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSAtomParser.m; sourceTree = ""; }; - 84469D211EFA3134004A6B28 /* RSParsedArticle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSParsedArticle.h; sourceTree = ""; }; - 84469D221EFA3134004A6B28 /* RSParsedArticle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSParsedArticle.m; sourceTree = ""; }; - 84469D231EFA3134004A6B28 /* RSParsedFeed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSParsedFeed.h; sourceTree = ""; }; - 84469D241EFA3134004A6B28 /* RSParsedFeed.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSParsedFeed.m; sourceTree = ""; }; - 84469D251EFA3134004A6B28 /* RSRSSParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSRSSParser.h; sourceTree = ""; }; - 84469D261EFA3134004A6B28 /* RSRSSParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSRSSParser.m; sourceTree = ""; }; - 84469D311EFA31CF004A6B28 /* FeedParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedParser.swift; path = Feeds/FeedParser.swift; sourceTree = ""; }; - 84469D331EFF1190004A6B28 /* NSData+RSParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+RSParser.h"; sourceTree = ""; }; - 84469D341EFF1190004A6B28 /* NSData+RSParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+RSParser.m"; sourceTree = ""; }; - 84469D371EFF2645004A6B28 /* RSSInJSONParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RSSInJSONParser.swift; path = Feeds/JSON/RSSInJSONParser.swift; sourceTree = ""; }; - 84469D3F1EFF29A9004A6B28 /* FeedParserError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedParserError.swift; path = Feeds/FeedParserError.swift; sourceTree = ""; }; - 84469D411EFF2B2D004A6B28 /* JSONTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONTypes.swift; sourceTree = ""; }; - 84469D431F002CEF004A6B28 /* JSONFeedParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONFeedParser.swift; path = Feeds/JSON/JSONFeedParser.swift; sourceTree = ""; }; - 844B5B3D1FE9A13B00C7C76A /* 4fsodonline.atom */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = 4fsodonline.atom; sourceTree = ""; }; - 844B5B3F1FE9A45200C7C76A /* expertopinionent.atom */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = expertopinionent.atom; sourceTree = ""; }; - 844B5B411FE9AF2700C7C76A /* RSParsedAuthor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RSParsedAuthor.h; sourceTree = ""; }; - 844B5B421FE9AF2700C7C76A /* RSParsedAuthor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RSParsedAuthor.m; sourceTree = ""; }; - 845213241FCB3C75003B6E93 /* coco.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = coco.html; sourceTree = ""; }; - 845213261FCB4042003B6E93 /* RSHTMLTag.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RSHTMLTag.h; sourceTree = ""; }; - 845213271FCB4042003B6E93 /* RSHTMLTag.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RSHTMLTag.m; sourceTree = ""; }; - 84566D931FD0ABFB00103322 /* allthis.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = allthis.json; sourceTree = ""; }; - 84566D951FD1FC1800103322 /* allthis-partial.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "allthis-partial.json"; sourceTree = ""; }; - 84628AAC1FCA10AE00566A9B /* allthis.atom */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = allthis.atom; sourceTree = ""; }; - 848674D11FCE7BF500802D1F /* macworld.rss */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = macworld.rss; sourceTree = ""; }; - 849A03C51F0081EA00122600 /* DaringFireball.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = DaringFireball.html; sourceTree = ""; }; - 849A03C61F0081EA00122600 /* DaringFireball.rss */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = DaringFireball.rss; sourceTree = ""; }; - 849A03C71F0081EA00122600 /* EMarley.rss */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = EMarley.rss; sourceTree = ""; }; - 849A03C81F0081EA00122600 /* furbo.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = furbo.html; sourceTree = ""; }; - 849A03C91F0081EA00122600 /* inessential.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = inessential.html; sourceTree = ""; }; - 849A03CA1F0081EA00122600 /* KatieFloyd.rss */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = KatieFloyd.rss; sourceTree = ""; }; - 849A03CB1F0081EA00122600 /* manton.rss */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = manton.rss; sourceTree = ""; }; - 849A03CC1F0081EA00122600 /* OneFootTsunami.atom */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = OneFootTsunami.atom; sourceTree = ""; }; - 849A03CD1F0081EA00122600 /* scriptingNews.rss */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = scriptingNews.rss; sourceTree = ""; }; - 849A03CE1F0081EA00122600 /* sixcolors.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = sixcolors.html; sourceTree = ""; }; - 849A03CF1F0081EA00122600 /* Subs.opml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Subs.opml; sourceTree = ""; }; - 849A03DD1F00837A00122600 /* FeedParserTypeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedParserTypeTests.swift; sourceTree = ""; }; - 849A03DF1F0088F600122600 /* OPMLTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OPMLTests.swift; sourceTree = ""; }; - 849A03E11F00902C00122600 /* RSDateParserTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSDateParserTests.m; sourceTree = ""; }; - 849A03E31F00912100122600 /* HTMLMetadataTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTMLMetadataTests.swift; sourceTree = ""; }; - 849A03E51F00952400122600 /* HTMLLinkTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTMLLinkTests.swift; sourceTree = ""; }; - 849A03E71F01F88600122600 /* ScriptingNews.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ScriptingNews.json; sourceTree = ""; }; - 849A03E91F01F92B00122600 /* inessential.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = inessential.json; sourceTree = ""; }; - 849A03EB1F01FCDC00122600 /* RSSInJSONParserTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSSInJSONParserTests.swift; sourceTree = ""; }; - 84B19A761FDA438300458981 /* natasha.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = natasha.xml; sourceTree = ""; }; - 84CF85BA2035455B0096F368 /* pxlnv.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = pxlnv.json; sourceTree = ""; }; - 84D81BD91EFA28E700652332 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 84D81BDA1EFA28E700652332 /* RSParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSParser.h; sourceTree = ""; }; - 84D81BDD1EFA2B7D00652332 /* ParsedFeed.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ParsedFeed.swift; path = Feeds/ParsedFeed.swift; sourceTree = ""; }; - 84D81BDF1EFA2BAE00652332 /* FeedType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedType.swift; path = Feeds/FeedType.swift; sourceTree = ""; }; - 84D81BE11EFA2D0900652332 /* ParsedAuthor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ParsedAuthor.swift; path = Feeds/ParsedAuthor.swift; sourceTree = ""; }; - 84D81BE31EFA2D3D00652332 /* ParsedItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ParsedItem.swift; path = Feeds/ParsedItem.swift; sourceTree = ""; }; - 84D81BE51EFA2DFB00652332 /* ParsedAttachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ParsedAttachment.swift; path = Feeds/ParsedAttachment.swift; sourceTree = ""; }; - 84D81BE71EFA2E6700652332 /* ParsedHub.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ParsedHub.swift; path = Feeds/ParsedHub.swift; sourceTree = ""; }; - 84DA2E20200415D500A4D03B /* curt.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = curt.json; sourceTree = ""; }; - 84DCCC651FF80E0100D2DDF1 /* EntityDecodingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityDecodingTests.swift; sourceTree = ""; }; - 84E7E69D1F85780D0046719D /* ParserData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ParserData.h; sourceTree = ""; }; - 84E7E69E1F85780D0046719D /* ParserData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ParserData.m; sourceTree = ""; }; - 84FF5F841EFA285800C15A01 /* RSParser.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RSParser.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 84FF5F8D1EFA285800C15A01 /* RSParserTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RSParserTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 84FF5F941EFA285800C15A01 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - D511EF0F20242E8700712EC3 /* RSParser_project_release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = RSParser_project_release.xcconfig; sourceTree = ""; }; - D511EF1020242E8700712EC3 /* RSParser_project.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = RSParser_project.xcconfig; sourceTree = ""; }; - D511EF1120242E8700712EC3 /* RSParserTests_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = RSParserTests_target.xcconfig; sourceTree = ""; }; - D511EF1220242E8700712EC3 /* RSParser_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = RSParser_target.xcconfig; sourceTree = ""; }; - D511EF1320242E8700712EC3 /* RSParser_project_debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = RSParser_project_debug.xcconfig; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 84FF5F801EFA285800C15A01 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 84285AAF1F006BC0002E8708 /* libxml2.2.tbd in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 84FF5F8A1EFA285800C15A01 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 84FF5F8E1EFA285800C15A01 /* RSParser.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 84245C861FDDDC470074AFBB /* JSON */ = { - isa = PBXGroup; - children = ( - 84469D411EFF2B2D004A6B28 /* JSONTypes.swift */, - 84245C871FDDDC6D0074AFBB /* JSONUtilities.swift */, - ); - path = JSON; - sourceTree = ""; - }; - 84285AA61F004879002E8708 /* Dates */ = { - isa = PBXGroup; - children = ( - 84469D121EFA30A2004A6B28 /* RSDateParser.h */, - 84469D131EFA30A2004A6B28 /* RSDateParser.m */, - ); - name = Dates; - sourceTree = ""; - }; - 84285AAD1F006BC0002E8708 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 84285AAE1F006BC0002E8708 /* libxml2.2.tbd */, - ); - name = Frameworks; - sourceTree = ""; - }; - 84469CE31EFA2FB0004A6B28 /* Feeds */ = { - isa = PBXGroup; - children = ( - 84D81BDF1EFA2BAE00652332 /* FeedType.swift */, - 84469D311EFA31CF004A6B28 /* FeedParser.swift */, - 84D81BDD1EFA2B7D00652332 /* ParsedFeed.swift */, - 84D81BE11EFA2D0900652332 /* ParsedAuthor.swift */, - 84D81BE31EFA2D3D00652332 /* ParsedItem.swift */, - 84D81BE51EFA2DFB00652332 /* ParsedAttachment.swift */, - 84D81BE71EFA2E6700652332 /* ParsedHub.swift */, - 84469D3F1EFF29A9004A6B28 /* FeedParserError.swift */, - 84469D391EFF2649004A6B28 /* JSON */, - 84469D1C1EFA3134004A6B28 /* XML */, - ); - name = Feeds; - sourceTree = ""; - }; - 84469CE41EFA3000004A6B28 /* OPML */ = { - isa = PBXGroup; - children = ( - 84469CED1EFA3000004A6B28 /* RSOPMLParser.h */, - 84469CEE1EFA3000004A6B28 /* RSOPMLParser.m */, - 84469CE71EFA3000004A6B28 /* RSOPMLDocument.h */, - 84469CE81EFA3000004A6B28 /* RSOPMLDocument.m */, - 84469CE51EFA3000004A6B28 /* RSOPMLAttributes.h */, - 84469CE61EFA3000004A6B28 /* RSOPMLAttributes.m */, - 84469CE91EFA3000004A6B28 /* RSOPMLFeedSpecifier.h */, - 84469CEA1EFA3000004A6B28 /* RSOPMLFeedSpecifier.m */, - 84469CEB1EFA3000004A6B28 /* RSOPMLItem.h */, - 84469CEC1EFA3000004A6B28 /* RSOPMLItem.m */, - 84285AB01F00702E002E8708 /* RSOPMLError.h */, - 84285AB11F00702E002E8708 /* RSOPMLError.m */, - ); - path = OPML; - sourceTree = ""; - }; - 84469CF91EFA3069004A6B28 /* SAX */ = { - isa = PBXGroup; - children = ( - 84469CFA1EFA3069004A6B28 /* RSSAXParser.h */, - 84469CFB1EFA3069004A6B28 /* RSSAXParser.m */, - ); - path = SAX; - sourceTree = ""; - }; - 84469CFE1EFA307E004A6B28 /* HTML */ = { - isa = PBXGroup; - children = ( - 84469CFF1EFA307E004A6B28 /* RSHTMLLinkParser.h */, - 84469D001EFA307E004A6B28 /* RSHTMLLinkParser.m */, - 84469D011EFA307E004A6B28 /* RSHTMLMetadata.h */, - 84469D021EFA307E004A6B28 /* RSHTMLMetadata.m */, - 84469D031EFA307E004A6B28 /* RSHTMLMetadataParser.h */, - 84469D041EFA307E004A6B28 /* RSHTMLMetadataParser.m */, - 84469D051EFA307E004A6B28 /* RSSAXHTMLParser.h */, - 84469D061EFA307E004A6B28 /* RSSAXHTMLParser.m */, - 845213261FCB4042003B6E93 /* RSHTMLTag.h */, - 845213271FCB4042003B6E93 /* RSHTMLTag.m */, - ); - path = HTML; - sourceTree = ""; - }; - 84469D0F1EFA30A2004A6B28 /* Utilities */ = { - isa = PBXGroup; - children = ( - 84469D331EFF1190004A6B28 /* NSData+RSParser.h */, - 84469D341EFF1190004A6B28 /* NSData+RSParser.m */, - 84469D101EFA30A2004A6B28 /* NSString+RSParser.h */, - 84469D111EFA30A2004A6B28 /* NSString+RSParser.m */, - 84469D141EFA30A2004A6B28 /* RSParserInternal.h */, - 84469D151EFA30A2004A6B28 /* RSParserInternal.m */, - ); - path = Utilities; - sourceTree = ""; - }; - 84469D1C1EFA3134004A6B28 /* XML */ = { - isa = PBXGroup; - children = ( - 84285AA71F005D53002E8708 /* RSSParser.swift */, - 84285AAB1F006754002E8708 /* AtomParser.swift */, - 84285AA91F006456002E8708 /* RSParsedFeedTransformer.swift */, - 84469D1D1EFA3134004A6B28 /* RSAtomParser.h */, - 84469D1E1EFA3134004A6B28 /* RSAtomParser.m */, - 84469D251EFA3134004A6B28 /* RSRSSParser.h */, - 84469D261EFA3134004A6B28 /* RSRSSParser.m */, - 84469D211EFA3134004A6B28 /* RSParsedArticle.h */, - 84469D221EFA3134004A6B28 /* RSParsedArticle.m */, - 84469D231EFA3134004A6B28 /* RSParsedFeed.h */, - 84469D241EFA3134004A6B28 /* RSParsedFeed.m */, - 8401FF7F1FE862E70080F13F /* RSParsedEnclosure.h */, - 8401FF801FE862E70080F13F /* RSParsedEnclosure.m */, - 844B5B411FE9AF2700C7C76A /* RSParsedAuthor.h */, - 844B5B421FE9AF2700C7C76A /* RSParsedAuthor.m */, - ); - name = XML; - path = Feeds/XML; - sourceTree = ""; - }; - 84469D391EFF2649004A6B28 /* JSON */ = { - isa = PBXGroup; - children = ( - 84469D371EFF2645004A6B28 /* RSSInJSONParser.swift */, - 84469D431F002CEF004A6B28 /* JSONFeedParser.swift */, - ); - name = JSON; - sourceTree = ""; - }; - 849A03C41F0081EA00122600 /* Resources */ = { - isa = PBXGroup; - children = ( - 84DA2E20200415D500A4D03B /* curt.json */, - 849A03C51F0081EA00122600 /* DaringFireball.html */, - 840FDCB71F0218670041F61B /* DaringFireball.atom */, - 840FDCB91F02186D0041F61B /* DaringFireball.json */, - 849A03C61F0081EA00122600 /* DaringFireball.rss */, - 842A42001FB77A5A00EA22E4 /* dcrainmaker.xml */, - 849A03C71F0081EA00122600 /* EMarley.rss */, - 849A03C81F0081EA00122600 /* furbo.html */, - 849A03C91F0081EA00122600 /* inessential.html */, - 849A03E91F01F92B00122600 /* inessential.json */, - 849A03CA1F0081EA00122600 /* KatieFloyd.rss */, - 848674D11FCE7BF500802D1F /* macworld.rss */, - 849A03CB1F0081EA00122600 /* manton.rss */, - 84B19A761FDA438300458981 /* natasha.xml */, - 849A03CC1F0081EA00122600 /* OneFootTsunami.atom */, - 849A03E71F01F88600122600 /* ScriptingNews.json */, - 849A03CD1F0081EA00122600 /* scriptingNews.rss */, - 845213241FCB3C75003B6E93 /* coco.html */, - 849A03CE1F0081EA00122600 /* sixcolors.html */, - 84628AAC1FCA10AE00566A9B /* allthis.atom */, - 84566D931FD0ABFB00103322 /* allthis.json */, - 84566D951FD1FC1800103322 /* allthis-partial.json */, - 8401FF831FE87C2E0080F13F /* theomnishow.rss */, - 844B5B3D1FE9A13B00C7C76A /* 4fsodonline.atom */, - 844B5B3F1FE9A45200C7C76A /* expertopinionent.atom */, - 84CF85BA2035455B0096F368 /* pxlnv.json */, - 849A03CF1F0081EA00122600 /* Subs.opml */, - ); - path = Resources; - sourceTree = ""; - }; - 84FF5F7A1EFA285800C15A01 = { - isa = PBXGroup; - children = ( - 84285AB41F007255002E8708 /* README.md */, - 84D81BDA1EFA28E700652332 /* RSParser.h */, - 84E7E69D1F85780D0046719D /* ParserData.h */, - 84E7E69E1F85780D0046719D /* ParserData.m */, - 84245C861FDDDC470074AFBB /* JSON */, - 84469CE31EFA2FB0004A6B28 /* Feeds */, - 84285AA61F004879002E8708 /* Dates */, - 84469CE41EFA3000004A6B28 /* OPML */, - 84469CFE1EFA307E004A6B28 /* HTML */, - 84469CF91EFA3069004A6B28 /* SAX */, - 84469D0F1EFA30A2004A6B28 /* Utilities */, - 84D81BD91EFA28E700652332 /* Info.plist */, - 84FF5F911EFA285800C15A01 /* RSParserTests */, - 84FF5F851EFA285800C15A01 /* Products */, - 84285AAD1F006BC0002E8708 /* Frameworks */, - D511EF0E20242E8700712EC3 /* xcconfig */, - ); - sourceTree = ""; - }; - 84FF5F851EFA285800C15A01 /* Products */ = { - isa = PBXGroup; - children = ( - 84FF5F841EFA285800C15A01 /* RSParser.framework */, - 84FF5F8D1EFA285800C15A01 /* RSParserTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 84FF5F911EFA285800C15A01 /* RSParserTests */ = { - isa = PBXGroup; - children = ( - 849A03DD1F00837A00122600 /* FeedParserTypeTests.swift */, - 849A03EB1F01FCDC00122600 /* RSSInJSONParserTests.swift */, - 840FDCB31F0217410041F61B /* JSONFeedParserTests.swift */, - 840FDCB11F01FED00041F61B /* RSSParserTests.swift */, - 840FDCB51F0217D20041F61B /* AtomParserTests.swift */, - 849A03DF1F0088F600122600 /* OPMLTests.swift */, - 849A03E11F00902C00122600 /* RSDateParserTests.m */, - 849A03E31F00912100122600 /* HTMLMetadataTests.swift */, - 849A03E51F00952400122600 /* HTMLLinkTests.swift */, - 84DCCC651FF80E0100D2DDF1 /* EntityDecodingTests.swift */, - 849A03C41F0081EA00122600 /* Resources */, - 84FF5F941EFA285800C15A01 /* Info.plist */, - ); - path = RSParserTests; - sourceTree = ""; - }; - D511EF0E20242E8700712EC3 /* xcconfig */ = { - isa = PBXGroup; - children = ( - D511EF1020242E8700712EC3 /* RSParser_project.xcconfig */, - D511EF1320242E8700712EC3 /* RSParser_project_debug.xcconfig */, - D511EF0F20242E8700712EC3 /* RSParser_project_release.xcconfig */, - D511EF1220242E8700712EC3 /* RSParser_target.xcconfig */, - D511EF1120242E8700712EC3 /* RSParserTests_target.xcconfig */, - ); - path = xcconfig; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 84FF5F811EFA285800C15A01 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 84469D271EFA3134004A6B28 /* RSAtomParser.h in Headers */, - 84469CF71EFA3000004A6B28 /* RSOPMLParser.h in Headers */, - 84469CEF1EFA3000004A6B28 /* RSOPMLAttributes.h in Headers */, - 84469CF51EFA3000004A6B28 /* RSOPMLItem.h in Headers */, - 84469D2D1EFA3134004A6B28 /* RSParsedFeed.h in Headers */, - 84469D181EFA30A2004A6B28 /* RSDateParser.h in Headers */, - 84469D1A1EFA30A2004A6B28 /* RSParserInternal.h in Headers */, - 84469D351EFF1190004A6B28 /* NSData+RSParser.h in Headers */, - 84D81BDC1EFA28E700652332 /* RSParser.h in Headers */, - 84469D0B1EFA307E004A6B28 /* RSHTMLMetadataParser.h in Headers */, - 84469CFC1EFA3069004A6B28 /* RSSAXParser.h in Headers */, - 845213281FCB4042003B6E93 /* RSHTMLTag.h in Headers */, - 84E7E69F1F85780D0046719D /* ParserData.h in Headers */, - 844B5B441FE9AFDB00C7C76A /* RSParsedAuthor.h in Headers */, - 8401FF811FE862E70080F13F /* RSParsedEnclosure.h in Headers */, - 84469D071EFA307E004A6B28 /* RSHTMLLinkParser.h in Headers */, - 84469D0D1EFA307E004A6B28 /* RSSAXHTMLParser.h in Headers */, - 84469D2B1EFA3134004A6B28 /* RSParsedArticle.h in Headers */, - 84469D2F1EFA3134004A6B28 /* RSRSSParser.h in Headers */, - 84469CF31EFA3000004A6B28 /* RSOPMLFeedSpecifier.h in Headers */, - 84469CF11EFA3000004A6B28 /* RSOPMLDocument.h in Headers */, - 84469D091EFA307E004A6B28 /* RSHTMLMetadata.h in Headers */, - 84285AB21F00702E002E8708 /* RSOPMLError.h in Headers */, - 84469D161EFA30A2004A6B28 /* NSString+RSParser.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 84FF5F831EFA285800C15A01 /* RSParser */ = { - isa = PBXNativeTarget; - buildConfigurationList = 84FF5F981EFA285800C15A01 /* Build configuration list for PBXNativeTarget "RSParser" */; - buildPhases = ( - 84FF5F7F1EFA285800C15A01 /* Sources */, - 84FF5F801EFA285800C15A01 /* Frameworks */, - 84FF5F811EFA285800C15A01 /* Headers */, - 84FF5F821EFA285800C15A01 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = RSParser; - productName = RSParser; - productReference = 84FF5F841EFA285800C15A01 /* RSParser.framework */; - productType = "com.apple.product-type.framework"; - }; - 84FF5F8C1EFA285800C15A01 /* RSParserTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 84FF5F9B1EFA285800C15A01 /* Build configuration list for PBXNativeTarget "RSParserTests" */; - buildPhases = ( - 84FF5F891EFA285800C15A01 /* Sources */, - 84FF5F8A1EFA285800C15A01 /* Frameworks */, - 84FF5F8B1EFA285800C15A01 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 84FF5F901EFA285800C15A01 /* PBXTargetDependency */, - ); - name = RSParserTests; - productName = RSParserTests; - productReference = 84FF5F8D1EFA285800C15A01 /* RSParserTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 84FF5F7B1EFA285800C15A01 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0900; - LastUpgradeCheck = 0930; - ORGANIZATIONNAME = "Ranchero Software, LLC"; - TargetAttributes = { - 84FF5F831EFA285800C15A01 = { - CreatedOnToolsVersion = 9.0; - LastSwiftMigration = 0900; - ProvisioningStyle = Automatic; - }; - 84FF5F8C1EFA285800C15A01 = { - CreatedOnToolsVersion = 9.0; - LastSwiftMigration = 0900; - ProvisioningStyle = Automatic; - }; - }; - }; - buildConfigurationList = 84FF5F7E1EFA285800C15A01 /* Build configuration list for PBXProject "RSParser" */; - compatibilityVersion = "Xcode 8.0"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - ); - mainGroup = 84FF5F7A1EFA285800C15A01; - productRefGroup = 84FF5F851EFA285800C15A01 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 84FF5F831EFA285800C15A01 /* RSParser */, - 84FF5F8C1EFA285800C15A01 /* RSParserTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 84FF5F821EFA285800C15A01 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 84FF5F8B1EFA285800C15A01 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 845213251FCB3C76003B6E93 /* coco.html in Resources */, - 849A03D51F0081EA00122600 /* KatieFloyd.rss in Resources */, - 849A03D81F0081EA00122600 /* scriptingNews.rss in Resources */, - 840FDCBA1F02186D0041F61B /* DaringFireball.json in Resources */, - 849A03D21F0081EA00122600 /* EMarley.rss in Resources */, - 849A03DA1F0081EA00122600 /* Subs.opml in Resources */, - 8401FF841FE87C2F0080F13F /* theomnishow.rss in Resources */, - 849A03D61F0081EA00122600 /* manton.rss in Resources */, - 849A03D11F0081EA00122600 /* DaringFireball.rss in Resources */, - 844B5B401FE9A45200C7C76A /* expertopinionent.atom in Resources */, - 84B19A771FDA438300458981 /* natasha.xml in Resources */, - 84566D961FD1FC1800103322 /* allthis-partial.json in Resources */, - 849A03D01F0081EA00122600 /* DaringFireball.html in Resources */, - 84566D941FD0ABFB00103322 /* allthis.json in Resources */, - 84628AAD1FCA10AE00566A9B /* allthis.atom in Resources */, - 848674D21FCE7BF600802D1F /* macworld.rss in Resources */, - 849A03EA1F01F92B00122600 /* inessential.json in Resources */, - 849A03D71F0081EA00122600 /* OneFootTsunami.atom in Resources */, - 849A03D41F0081EA00122600 /* inessential.html in Resources */, - 84DA2E21200415D500A4D03B /* curt.json in Resources */, - 849A03D31F0081EA00122600 /* furbo.html in Resources */, - 849A03E81F01F88600122600 /* ScriptingNews.json in Resources */, - 84CF85BB2035455B0096F368 /* pxlnv.json in Resources */, - 844B5B3E1FE9A13C00C7C76A /* 4fsodonline.atom in Resources */, - 840FDCB81F0218670041F61B /* DaringFireball.atom in Resources */, - 849A03D91F0081EA00122600 /* sixcolors.html in Resources */, - 842A42011FB77A5A00EA22E4 /* dcrainmaker.xml in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 84FF5F7F1EFA285800C15A01 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 84469D081EFA307E004A6B28 /* RSHTMLLinkParser.m in Sources */, - 84469D1B1EFA30A2004A6B28 /* RSParserInternal.m in Sources */, - 84D81BE21EFA2D0900652332 /* ParsedAuthor.swift in Sources */, - 84469D0E1EFA307E004A6B28 /* RSSAXHTMLParser.m in Sources */, - 84469CF41EFA3000004A6B28 /* RSOPMLFeedSpecifier.m in Sources */, - 84469CF01EFA3000004A6B28 /* RSOPMLAttributes.m in Sources */, - 84469D381EFF2645004A6B28 /* RSSInJSONParser.swift in Sources */, - 84E7E6A01F85780D0046719D /* ParserData.m in Sources */, - 84245C881FDDDC6D0074AFBB /* JSONUtilities.swift in Sources */, - 84469D301EFA3134004A6B28 /* RSRSSParser.m in Sources */, - 84469D191EFA30A2004A6B28 /* RSDateParser.m in Sources */, - 84469CFD1EFA3069004A6B28 /* RSSAXParser.m in Sources */, - 84469CF21EFA3000004A6B28 /* RSOPMLDocument.m in Sources */, - 84469CF61EFA3000004A6B28 /* RSOPMLItem.m in Sources */, - 84D81BE41EFA2D3D00652332 /* ParsedItem.swift in Sources */, - 84285AAC1F006754002E8708 /* AtomParser.swift in Sources */, - 84285AA81F005D53002E8708 /* RSSParser.swift in Sources */, - 84469D421EFF2B2D004A6B28 /* JSONTypes.swift in Sources */, - 844B5B451FE9AFE000C7C76A /* RSParsedAuthor.m in Sources */, - 84469D0C1EFA307E004A6B28 /* RSHTMLMetadataParser.m in Sources */, - 84469D0A1EFA307E004A6B28 /* RSHTMLMetadata.m in Sources */, - 84469D171EFA30A2004A6B28 /* NSString+RSParser.m in Sources */, - 845213291FCB4042003B6E93 /* RSHTMLTag.m in Sources */, - 84469D2C1EFA3134004A6B28 /* RSParsedArticle.m in Sources */, - 84285AAA1F006456002E8708 /* RSParsedFeedTransformer.swift in Sources */, - 84469D2E1EFA3134004A6B28 /* RSParsedFeed.m in Sources */, - 84469CF81EFA3000004A6B28 /* RSOPMLParser.m in Sources */, - 8401FF821FE862E70080F13F /* RSParsedEnclosure.m in Sources */, - 84469D401EFF29A9004A6B28 /* FeedParserError.swift in Sources */, - 84469D321EFA31CF004A6B28 /* FeedParser.swift in Sources */, - 84469D281EFA3134004A6B28 /* RSAtomParser.m in Sources */, - 84469D361EFF1190004A6B28 /* NSData+RSParser.m in Sources */, - 84D81BE61EFA2DFB00652332 /* ParsedAttachment.swift in Sources */, - 84285AB31F00702E002E8708 /* RSOPMLError.m in Sources */, - 84D81BDE1EFA2B7D00652332 /* ParsedFeed.swift in Sources */, - 84D81BE81EFA2E6700652332 /* ParsedHub.swift in Sources */, - 84469D441F002CEF004A6B28 /* JSONFeedParser.swift in Sources */, - 84D81BE01EFA2BAE00652332 /* FeedType.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 84FF5F891EFA285800C15A01 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 849A03EC1F01FCDC00122600 /* RSSInJSONParserTests.swift in Sources */, - 849A03E01F0088F600122600 /* OPMLTests.swift in Sources */, - 840FDCB41F0217410041F61B /* JSONFeedParserTests.swift in Sources */, - 84DCCC661FF80E0100D2DDF1 /* EntityDecodingTests.swift in Sources */, - 849A03E61F00952400122600 /* HTMLLinkTests.swift in Sources */, - 849A03E21F00902C00122600 /* RSDateParserTests.m in Sources */, - 849A03DE1F00837A00122600 /* FeedParserTypeTests.swift in Sources */, - 840FDCB21F01FED00041F61B /* RSSParserTests.swift in Sources */, - 840FDCB61F0217D20041F61B /* AtomParserTests.swift in Sources */, - 849A03E41F00912100122600 /* HTMLMetadataTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 84FF5F901EFA285800C15A01 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 84FF5F831EFA285800C15A01 /* RSParser */; - targetProxy = 84FF5F8F1EFA285800C15A01 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 84FF5F961EFA285800C15A01 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D511EF1320242E8700712EC3 /* RSParser_project_debug.xcconfig */; - buildSettings = { - }; - name = Debug; - }; - 84FF5F971EFA285800C15A01 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D511EF0F20242E8700712EC3 /* RSParser_project_release.xcconfig */; - buildSettings = { - }; - name = Release; - }; - 84FF5F991EFA285800C15A01 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D511EF1220242E8700712EC3 /* RSParser_target.xcconfig */; - buildSettings = { - }; - name = Debug; - }; - 84FF5F9A1EFA285800C15A01 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D511EF1220242E8700712EC3 /* RSParser_target.xcconfig */; - buildSettings = { - }; - name = Release; - }; - 84FF5F9C1EFA285800C15A01 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D511EF1120242E8700712EC3 /* RSParserTests_target.xcconfig */; - buildSettings = { - }; - name = Debug; - }; - 84FF5F9D1EFA285800C15A01 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D511EF1120242E8700712EC3 /* RSParserTests_target.xcconfig */; - buildSettings = { - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 84FF5F7E1EFA285800C15A01 /* Build configuration list for PBXProject "RSParser" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 84FF5F961EFA285800C15A01 /* Debug */, - 84FF5F971EFA285800C15A01 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 84FF5F981EFA285800C15A01 /* Build configuration list for PBXNativeTarget "RSParser" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 84FF5F991EFA285800C15A01 /* Debug */, - 84FF5F9A1EFA285800C15A01 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 84FF5F9B1EFA285800C15A01 /* Build configuration list for PBXNativeTarget "RSParserTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 84FF5F9C1EFA285800C15A01 /* Debug */, - 84FF5F9D1EFA285800C15A01 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 84FF5F7B1EFA285800C15A01 /* Project object */; -} diff --git a/Frameworks/RSParser/RSParser.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Frameworks/RSParser/RSParser.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 9a349e055..000000000 --- a/Frameworks/RSParser/RSParser.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/Frameworks/RSParser/RSParserTests/AtomParserTests.swift b/Frameworks/RSParser/RSParserTests/AtomParserTests.swift deleted file mode 100644 index 2763c6912..000000000 --- a/Frameworks/RSParser/RSParserTests/AtomParserTests.swift +++ /dev/null @@ -1,110 +0,0 @@ -// -// AtomParserTests.swift -// RSParser -// -// Created by Brent Simmons on 6/26/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import XCTest -import RSParser - -class AtomParserTests: XCTestCase { - - func testDaringFireballPerformance() { - - // 0.009 sec on my 2012 iMac. - let d = parserData("DaringFireball", "atom", "http://daringfireball.net/") //It’s actually an Atom feed - self.measure { - let _ = try! FeedParser.parse(d) - } - } - - func testAllThisPerformance() { - - // 0.003 sec on my 2012 iMac. - let d = parserData("allthis", "atom", "http://leancrew.com/all-this") - self.measure { - let _ = try! FeedParser.parse(d) - } - } - - func testGettingHomePageLink() { - - let d = parserData("allthis", "atom", "http://leancrew.com/all-this") - let parsedFeed = try! FeedParser.parse(d)! - - XCTAssertTrue(parsedFeed.homePageURL == "http://leancrew.com/all-this") - } - - func testDaringFireball() { - - let d = parserData("DaringFireball", "atom", "http://daringfireball.net/") //It’s actually an Atom feed - let parsedFeed = try! FeedParser.parse(d)! - - for article in parsedFeed.items { - - XCTAssertNotNil(article.externalURL) - - if !article.title!.hasPrefix("★ ") { - XCTAssertNotNil(article.url) - XCTAssert(article.url!.hasPrefix("https://daringfireball.net/")) - } - - XCTAssertTrue(article.uniqueID.hasPrefix("tag:daringfireball.net,2017:/")) - - XCTAssertEqual(article.authors!.count, 1) // TODO: parse Atom authors - let author = article.authors!.first! - if author.name == "Daring Fireball Department of Commerce" { - XCTAssertNil(author.url) - } - else { - XCTAssertEqual(author.name, "John Gruber") - XCTAssertEqual(author.url, "http://daringfireball.net/") - } - - XCTAssertNotNil(article.datePublished) - XCTAssert(article.attachments == nil) - } - } - - func test4fsodonlineAttachments() { - - // Thanks to Marco for finding me some Atom podcast feeds. Apparently they’re super-rare. - - let d = parserData("4fsodonline", "atom", "http://4fsodonline.blogspot.com/") - let parsedFeed = try! FeedParser.parse(d)! - - for article in parsedFeed.items { - - XCTAssertTrue(article.attachments!.count > 0) - let attachment = article.attachments!.first! - - XCTAssertTrue(attachment.url.hasPrefix("http://www.blogger.com/video-play.mp4?")) - XCTAssertNil(attachment.sizeInBytes) - XCTAssertEqual(attachment.mimeType!, "video/mp4") - } - } - - func testExpertOpinionENTAttachments() { - - // Another from Marco. - - let d = parserData("expertopinionent", "atom", "http://expertopinionent.typepad.com/my-blog/") - let parsedFeed = try! FeedParser.parse(d)! - - for article in parsedFeed.items { - - guard let attachments = article.attachments else { - continue - } - - XCTAssertEqual(attachments.count, 1) - let attachment = attachments.first! - - XCTAssertTrue(attachment.url.hasSuffix(".mp3")) - XCTAssertNil(attachment.sizeInBytes) - XCTAssertEqual(attachment.mimeType!, "audio/mpeg") - } - } -} diff --git a/Frameworks/RSParser/RSParserTests/EntityDecodingTests.swift b/Frameworks/RSParser/RSParserTests/EntityDecodingTests.swift deleted file mode 100644 index a40c8a5b9..000000000 --- a/Frameworks/RSParser/RSParserTests/EntityDecodingTests.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// EntityDecodingTests.swift -// RSParserTests -// -// Created by Brent Simmons on 12/30/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import XCTest -import RSParser - -class EntityDecodingTests: XCTestCase { - - func test39Decoding() { - - // Bug found by Manton Reece — the ' entity was not getting decoded by Evergreen in JSON Feeds from micro.blog. - - let s = "These are the times that try men's souls." - let decoded = s.rsparser_stringByDecodingHTMLEntities() - - XCTAssertEqual(decoded, "These are the times that try men's souls.") - } -} diff --git a/Frameworks/RSParser/RSParserTests/FeedParserTypeTests.swift b/Frameworks/RSParser/RSParserTests/FeedParserTypeTests.swift deleted file mode 100644 index e7bbb124c..000000000 --- a/Frameworks/RSParser/RSParserTests/FeedParserTypeTests.swift +++ /dev/null @@ -1,213 +0,0 @@ -// -// FeedParserTypeTests.swift -// RSParser -// -// Created by Brent Simmons on 6/25/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import XCTest -import RSParser - -class FeedParserTypeTests: XCTestCase { - - // MARK: HTML - - func testDaringFireballHTMLType() { - - let d = parserData("DaringFireball", "html", "http://daringfireball.net/") - let type = feedType(d) - XCTAssertTrue(type == .notAFeed) - } - - func testFurboHTMLType() { - - let d = parserData("furbo", "html", "http://furbo.org/") - let type = feedType(d) - XCTAssertTrue(type == .notAFeed) - } - - func testInessentialHTMLType() { - - let d = parserData("inessential", "html", "http://inessential.com/") - let type = feedType(d) - XCTAssertTrue(type == .notAFeed) - } - - func testSixColorsHTMLType() { - - let d = parserData("sixcolors", "html", "https://sixcolors.com/") - let type = feedType(d) - XCTAssertTrue(type == .notAFeed) - } - - // MARK: RSS - - func testEMarleyRSSType() { - - let d = parserData("EMarley", "rss", "https://medium.com/@emarley") - let type = feedType(d) - XCTAssertTrue(type == .rss) - } - - func testScriptingNewsRSSType() { - - let d = parserData("scriptingNews", "rss", "http://scripting.com/") - let type = feedType(d) - XCTAssertTrue(type == .rss) - } - - func testKatieFloydRSSType() { - - let d = parserData("KatieFloyd", "rss", "https://katiefloyd.com/") - let type = feedType(d) - XCTAssertTrue(type == .rss) - } - - func testMantonRSSType() { - - let d = parserData("manton", "rss", "http://manton.org/") - let type = feedType(d) - XCTAssertTrue(type == .rss) - } - - func testDCRainmakerRSSType() { - - let d = parserData("dcrainmaker", "xml", "https://www.dcrainmaker.com/") - let type = feedType(d) - XCTAssertTrue(type == .rss) - } - - func testMacworldRSSType() { - - let d = parserData("macworld", "rss", "https://www.macworld.com/") - let type = feedType(d) - XCTAssertTrue(type == .rss) - } - - func testNatashaTheRobotRSSType() { - - let d = parserData("natasha", "xml", "https://www.natashatherobot.com/") - let type = feedType(d) - XCTAssertTrue(type == .rss) - } - - // MARK: Atom - - func testDaringFireballAtomType() { - - // File extension is .rss, but it’s really an Atom feed. - let d = parserData("DaringFireball", "rss", "http://daringfireball.net/") - let type = feedType(d) - XCTAssertTrue(type == .atom) - } - - func testOneFootTsunamiAtomType() { - - let d = parserData("OneFootTsunami", "atom", "http://onefoottsunami.com/") - let type = feedType(d) - XCTAssertTrue(type == .atom) - } - - // MARK: RSS-in-JSON - - func testScriptingNewsJSONType() { - - let d = parserData("ScriptingNews", "json", "http://scripting.com/") - let type = feedType(d) - XCTAssertTrue(type == .rssInJSON) - } - - // MARK: JSON Feed - - func testInessentialJSONFeedType() { - - let d = parserData("inessential", "json", "http://inessential.com/") - let type = feedType(d) - XCTAssertTrue(type == .jsonFeed) - } - - func testAllThisJSONFeedType() { - - let d = parserData("allthis", "json", "http://leancrew.com/allthis/") - let type = feedType(d) - XCTAssertTrue(type == .jsonFeed) - } - - func testCurtJSONFeedType() { - - let d = parserData("curt", "json", "http://curtclifton.net/") - let type = feedType(d) - XCTAssertTrue(type == .jsonFeed) - } - - func testPixelEnvyJSONFeedType() { - - let d = parserData("pxlnv", "json", "http://pxlnv.com/") - let type = feedType(d) - XCTAssertTrue(type == .jsonFeed) - } - - // MARK: Unknown - - func testPartialAllThisUnknownFeedType() { - - // In the case of this feed, the partial data isn’t enough to detect that it’s a JSON Feed. - // The type detector should return .unknown rather than .notAFeed. - - let d = parserData("allthis-partial", "json", "http://leancrew.com/allthis/") - let type = feedType(d, isPartialData: true) - XCTAssertEqual(type, .unknown) - } - - // MARK: Performance - - func testFeedTypePerformance() { - - // 0.000 on my 2012 iMac. - - let d = parserData("EMarley", "rss", "https://medium.com/@emarley") - self.measure { - let _ = feedType(d) - } - } - - func testFeedTypePerformance2() { - - // 0.000 on my 2012 iMac. - - let d = parserData("inessential", "json", "http://inessential.com/") - self.measure { - let _ = feedType(d) - } - } - - func testFeedTypePerformance3() { - - // 0.000 on my 2012 iMac. - - let d = parserData("DaringFireball", "html", "http://daringfireball.net/") - self.measure { - let _ = feedType(d) - } - } - - func testFeedTypePerformance4() { - - // 0.001 on my 2012 iMac. - - let d = parserData("DaringFireball", "rss", "http://daringfireball.net/") - self.measure { - let _ = feedType(d) - } - } - -} - -func parserData(_ filename: String, _ fileExtension: String, _ url: String) -> ParserData { - - let bundle = Bundle(for: FeedParserTypeTests.self) - let path = bundle.path(forResource: filename, ofType: fileExtension)! - let data = try! Data(contentsOf: URL(fileURLWithPath: path)) - return ParserData(url: url, data: data) -} diff --git a/Frameworks/RSParser/RSParserTests/HTMLLinkTests.swift b/Frameworks/RSParser/RSParserTests/HTMLLinkTests.swift deleted file mode 100644 index 545a669b6..000000000 --- a/Frameworks/RSParser/RSParserTests/HTMLLinkTests.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// HTMLLinkTests.swift -// RSParser -// -// Created by Brent Simmons on 6/25/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import XCTest -import RSParser - -class HTMLLinkTests: XCTestCase { - - func testSixColorsPerformance() { - - // 0.003 sec on my 2012 iMac - let d = parserData("sixcolors", "html", "http://sixcolors.com/") - self.measure { - let _ = RSHTMLLinkParser.htmlLinks(with: d) - } - } - - func testSixColorsLink() { - - let d = parserData("sixcolors", "html", "http://sixcolors.com/") - let links = RSHTMLLinkParser.htmlLinks(with: d)! - - let linkToFind = "https://www.theincomparable.com/theincomparable/290/index.php" - let textToFind = "this week’s episode of The Incomparable" - - var found = false - for oneLink in links { - if let urlString = oneLink.urlString, let text = oneLink.text, urlString == linkToFind, text == textToFind { - found = true - } - } - - XCTAssertTrue(found) - XCTAssertEqual(links.count, 131) - } - -} diff --git a/Frameworks/RSParser/RSParserTests/HTMLMetadataTests.swift b/Frameworks/RSParser/RSParserTests/HTMLMetadataTests.swift deleted file mode 100644 index a03f8bcc6..000000000 --- a/Frameworks/RSParser/RSParserTests/HTMLMetadataTests.swift +++ /dev/null @@ -1,141 +0,0 @@ -// -// HTMLMetadataTests.swift -// RSParser -// -// Created by Brent Simmons on 6/25/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import XCTest -import RSParser - -class HTMLMetadataTests: XCTestCase { - - func testDaringFireball() { - - let d = parserData("DaringFireball", "html", "http://daringfireball.net/") - let metadata = RSHTMLMetadataParser.htmlMetadata(with: d) - - XCTAssertEqual(metadata.faviconLink, "http://daringfireball.net/graphics/favicon.ico?v=005") - - XCTAssertEqual(metadata.feedLinks.count, 1) - - let feedLink = metadata.feedLinks.first! - XCTAssertNil(feedLink.title) - XCTAssertEqual(feedLink.type, "application/atom+xml") - XCTAssertEqual(feedLink.urlString, "http://daringfireball.net/feeds/main") - } - - func testDaringFireballPerformance() { - - // 0.002 sec on my 2012 iMac - let d = parserData("DaringFireball", "html", "http://daringfireball.net/") - self.measure { - let _ = RSHTMLMetadataParser.htmlMetadata(with: d) - } - } - - func testFurbo() { - - let d = parserData("furbo", "html", "http://furbo.org/") - let metadata = RSHTMLMetadataParser.htmlMetadata(with: d) - - XCTAssertEqual(metadata.faviconLink, "http://furbo.org/favicon.ico") - - XCTAssertEqual(metadata.feedLinks.count, 1) - - let feedLink = metadata.feedLinks.first! - XCTAssertEqual(feedLink.title, "Iconfactory News Feed") - XCTAssertEqual(feedLink.type, "application/rss+xml") - } - - func testFurboPerformance() { - - // 0.001 sec on my 2012 iMac - let d = parserData("furbo", "html", "http://furbo.org/") - self.measure { - let _ = RSHTMLMetadataParser.htmlMetadata(with: d) - } - } - - func testInessential() { - - let d = parserData("inessential", "html", "http://inessential.com/") - let metadata = RSHTMLMetadataParser.htmlMetadata(with: d) - - XCTAssertNil(metadata.faviconLink) - - XCTAssertEqual(metadata.feedLinks.count, 1) - let feedLink = metadata.feedLinks.first! - XCTAssertEqual(feedLink.title, "RSS") - XCTAssertEqual(feedLink.type, "application/rss+xml") - XCTAssertEqual(feedLink.urlString, "http://inessential.com/xml/rss.xml") - - XCTAssertEqual(metadata.appleTouchIcons.count, 0); - } - - func testInessentialPerformance() { - - // 0.001 sec on my 2012 iMac - let d = parserData("inessential", "html", "http://inessential.com/") - self.measure { - let _ = RSHTMLMetadataParser.htmlMetadata(with: d) - } - } - - func testCocoPerformance() { - - // 0.004 sec on my 2012 iMac - let d = parserData("coco", "html", "https://www.theatlantic.com/entertainment/archive/2017/11/coco-is-among-pixars-best-movies-in-years/546695/") - self.measure { - let _ = RSHTMLMetadataParser.htmlMetadata(with: d) - } - } - - func testSixColors() { - - let d = parserData("sixcolors", "html", "http://sixcolors.com/") - let metadata = RSHTMLMetadataParser.htmlMetadata(with: d) - - XCTAssertEqual(metadata.faviconLink, "https://sixcolors.com/images/favicon.ico") - - XCTAssertEqual(metadata.feedLinks.count, 1); - let feedLink = metadata.feedLinks.first! - XCTAssertEqual(feedLink.title, "RSS"); - XCTAssertEqual(feedLink.type, "application/rss+xml"); - XCTAssertEqual(feedLink.urlString, "http://feedpress.me/sixcolors"); - - XCTAssertEqual(metadata.appleTouchIcons.count, 6); - let icon = metadata.appleTouchIcons[3]; - XCTAssertEqual(icon.rel, "apple-touch-icon"); - XCTAssertEqual(icon.sizes, "120x120"); - XCTAssertEqual(icon.urlString, "https://sixcolors.com/apple-touch-icon-120.png"); - } - - func testSixColorsPerformance() { - - // 0.002 sec on my 2012 iMac - let d = parserData("sixcolors", "html", "http://sixcolors.com/") - self.measure { - let _ = RSHTMLMetadataParser.htmlMetadata(with: d) - } - } - - func testCocoOGImage() { - - let d = parserData("coco", "html", "https://www.theatlantic.com/entertainment/archive/2017/11/coco-is-among-pixars-best-movies-in-years/546695/") - let metadata = RSHTMLMetadataParser.htmlMetadata(with: d) - let openGraphData = metadata.openGraphProperties! - let image = openGraphData.images.first! - XCTAssert(image.url == "https://cdn.theatlantic.com/assets/media/img/mt/2017/11/1033101_first_full_length_trailer_arrives_pixars_coco/facebook.jpg?1511382177") - } - - func testCocoTwitterImage() { - - let d = parserData("coco", "html", "https://www.theatlantic.com/entertainment/archive/2017/11/coco-is-among-pixars-best-movies-in-years/546695/") - let metadata = RSHTMLMetadataParser.htmlMetadata(with: d) - let twitterData = metadata.twitterProperties! - let imageURL = twitterData.imageURL! - XCTAssert(imageURL == "https://cdn.theatlantic.com/assets/media/img/mt/2017/11/1033101_first_full_length_trailer_arrives_pixars_coco/facebook.jpg?1511382177") - } -} diff --git a/Frameworks/RSParser/RSParserTests/Info.plist b/Frameworks/RSParser/RSParserTests/Info.plist deleted file mode 100644 index 6c40a6cd0..000000000 --- a/Frameworks/RSParser/RSParserTests/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/Frameworks/RSParser/RSParserTests/JSONFeedParserTests.swift b/Frameworks/RSParser/RSParserTests/JSONFeedParserTests.swift deleted file mode 100644 index c68cdc260..000000000 --- a/Frameworks/RSParser/RSParserTests/JSONFeedParserTests.swift +++ /dev/null @@ -1,95 +0,0 @@ -// -// JSONFeedParserTests.swift -// RSParser -// -// Created by Brent Simmons on 6/26/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import XCTest -import RSParser - -class JSONFeedParserTests: XCTestCase { - - func testInessentialPerformance() { - - // 0.001 sec on my 2012 iMac. - let d = parserData("inessential", "json", "http://inessential.com/") - self.measure { - let _ = try! FeedParser.parse(d) - } - } - - func testDaringFireballPerformance() { - - // 0.009 sec on my 2012 iMac. - let d = parserData("DaringFireball", "json", "http://daringfireball.net/") - self.measure { - let _ = try! FeedParser.parse(d) - } - } - - func testThatEntitiesAreDecoded() { - - let d = parserData("DaringFireball", "json", "http://daringfireball.net/") - let parsedFeed = try! FeedParser.parse(d) - - // https://github.com/brentsimmons/Evergreen/issues/176 - // In the article titled "The Talk Show: ‘I Do Like Throwing a Baby’", - // make sure the content HTML starts with "\n

New episode of America’s" - // instead of "\n

New episode of America’s" — this will tell us - // that entities are being decoded. - - for article in parsedFeed!.items { - if article.title == "The Talk Show: ‘I Do Like Throwing a Baby’" { - XCTAssert(article.contentHTML!.hasPrefix("\n

New episode of America’s")) - return - } - } - - XCTAssert(false, "Expected to find “The Talk Show: ‘I Do Like Throwing a Baby’” article.") - } - - func testGettingFaviconAndIconURLs() { - - let d = parserData("DaringFireball", "json", "http://daringfireball.net/") - let parsedFeed = try! FeedParser.parse(d)! - - XCTAssert(parsedFeed.faviconURL == "https://daringfireball.net/graphics/favicon-64.png") - XCTAssert(parsedFeed.iconURL == "https://daringfireball.net/graphics/apple-touch-icon.png") - } - - func testAllThis() { - - let d = parserData("allthis", "json", "http://leancrew.com/allthis/") - let parsedFeed = try! FeedParser.parse(d)! - - XCTAssertEqual(parsedFeed.items.count, 12) - } - - func testCurt() { - - let d = parserData("curt", "json", "http://curtclifton.net/") - let parsedFeed = try! FeedParser.parse(d)! - - XCTAssertEqual(parsedFeed.items.count, 26) - - var didFindTwitterQuitterArticle = false - for article in parsedFeed.items { - if article.title == "Twitter Quitter" { - didFindTwitterQuitterArticle = true - XCTAssertTrue(article.contentHTML!.hasPrefix("

I’ve decided to close my Twitter account. William Van Hecke makes a convincing case")) - } - } - - XCTAssertTrue(didFindTwitterQuitterArticle) - } - - func testPixelEnvy() { - - let d = parserData("pxlnv", "json", "http://pxlnv.com/") - let parsedFeed = try! FeedParser.parse(d)! - XCTAssertEqual(parsedFeed.items.count, 20) - - } -} diff --git a/Frameworks/RSParser/RSParserTests/OPMLTests.swift b/Frameworks/RSParser/RSParserTests/OPMLTests.swift deleted file mode 100644 index 5dca5c135..000000000 --- a/Frameworks/RSParser/RSParserTests/OPMLTests.swift +++ /dev/null @@ -1,66 +0,0 @@ -// -// OPMLTests.swift -// RSParser -// -// Created by Brent Simmons on 6/25/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import XCTest -import RSParser - -class OPMLTests: XCTestCase { - - let subsData = parserData("Subs", "opml", "http://example.org/") - - func testOPMLParsingPerformance() { - - // 0.002 sec on my 2012 iMac. - self.measure { - let _ = try! RSOPMLParser.parseOPML(with: self.subsData) - } - } - - func testNotOPML() { - - let d = parserData("DaringFireball", "rss", "http://daringfireball.net/") - XCTAssertThrowsError(try RSOPMLParser.parseOPML(with: d)) - } - - func testSubsStructure() { - - let opmlDocument = try! RSOPMLParser.parseOPML(with: subsData) - recursivelyCheckOPMLStructure(opmlDocument) - } - - func recursivelyCheckOPMLStructure(_ item: RSOPMLItem) { - - let feedSpecifier = item.feedSpecifier - if !(item is RSOPMLDocument) { - XCTAssertNotNil((item.attributes! as NSDictionary).opml_text) - } - - // If it has no children, it should have a feed specifier. The converse is also true. - var isFolder = item.children != nil && item.children!.count > 0 - if !isFolder && (item.attributes! as NSDictionary).opml_title == "Skip" { - isFolder = true - } - - if !isFolder { - XCTAssertNotNil(feedSpecifier!.title) - XCTAssertNotNil(feedSpecifier!.feedURL) - } - else { - XCTAssertNil(feedSpecifier) - if !(item is RSOPMLDocument) { - XCTAssertNotNil((item.attributes! as NSDictionary).opml_title) - } - } - - if item.children != nil && item.children!.count > 0 { - for oneItem in item.children! { - recursivelyCheckOPMLStructure(oneItem) - } - } - } -} diff --git a/Frameworks/RSParser/RSParserTests/RSDateParserTests.m b/Frameworks/RSParser/RSParserTests/RSDateParserTests.m deleted file mode 100755 index 58f1116e3..000000000 --- a/Frameworks/RSParser/RSParserTests/RSDateParserTests.m +++ /dev/null @@ -1,98 +0,0 @@ -// -// RSDateParserTests.m -// RSParser -// -// Created by Brent Simmons on 12/26/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -#import -@import RSParser; - -@interface RSDateParserTests : XCTestCase - -@end - -@implementation RSDateParserTests - -static NSDate *dateWithValues(NSInteger year, NSInteger month, NSInteger day, NSInteger hour, NSInteger minute, NSInteger second) { - - NSDateComponents *dateComponents = [[NSDateComponents alloc] init]; - dateComponents.calendar = NSCalendar.currentCalendar; - dateComponents.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; - [dateComponents setValue:year forComponent:NSCalendarUnitYear]; - [dateComponents setValue:month forComponent:NSCalendarUnitMonth]; - [dateComponents setValue:day forComponent:NSCalendarUnitDay]; - [dateComponents setValue:hour forComponent:NSCalendarUnitHour]; - [dateComponents setValue:minute forComponent:NSCalendarUnitMinute]; - [dateComponents setValue:second forComponent:NSCalendarUnitSecond]; - - return dateComponents.date; -} - -- (void)testDateWithString { - - NSDate *expectedDateResult = dateWithValues(2010, 5, 28, 21, 3, 38); - XCTAssertNotNil(expectedDateResult); - - NSDate *d = RSDateWithString(@"Fri, 28 May 2010 21:03:38 +0000"); - XCTAssertEqualObjects(d, expectedDateResult); - - d = RSDateWithString(@"Fri, 28 May 2010 21:03:38 +00:00"); - XCTAssertEqualObjects(d, expectedDateResult); - - d = RSDateWithString(@"Fri, 28 May 2010 21:03:38 -00:00"); - XCTAssertEqualObjects(d, expectedDateResult); - - d = RSDateWithString(@"Fri, 28 May 2010 21:03:38 -0000"); - XCTAssertEqualObjects(d, expectedDateResult); - - d = RSDateWithString(@"Fri, 28 May 2010 21:03:38 GMT"); - XCTAssertEqualObjects(d, expectedDateResult); - - d = RSDateWithString(@"2010-05-28T21:03:38+00:00"); - XCTAssertEqualObjects(d, expectedDateResult); - - d = RSDateWithString(@"2010-05-28T21:03:38+0000"); - XCTAssertEqualObjects(d, expectedDateResult); - - d = RSDateWithString(@"2010-05-28T21:03:38-0000"); - XCTAssertEqualObjects(d, expectedDateResult); - - d = RSDateWithString(@"2010-05-28T21:03:38-00:00"); - XCTAssertEqualObjects(d, expectedDateResult); - - d = RSDateWithString(@"2010-05-28T21:03:38Z"); - XCTAssertEqualObjects(d, expectedDateResult); - - expectedDateResult = dateWithValues(2010, 7, 13, 17, 6, 40); - d = RSDateWithString(@"2010-07-13T17:06:40+00:00"); - XCTAssertEqualObjects(d, expectedDateResult); - - expectedDateResult = dateWithValues(2010, 4, 30, 12, 0, 0); - d = RSDateWithString(@"30 Apr 2010 5:00 PDT"); - XCTAssertEqualObjects(d, expectedDateResult); - - expectedDateResult = dateWithValues(2010, 5, 21, 21, 22, 53); - d = RSDateWithString(@"21 May 2010 21:22:53 GMT"); - XCTAssertEqualObjects(d, expectedDateResult); - - expectedDateResult = dateWithValues(2010, 6, 9, 5, 0, 0); - d = RSDateWithString(@"Wed, 09 Jun 2010 00:00 EST"); - XCTAssertEqualObjects(d, expectedDateResult); - - expectedDateResult = dateWithValues(2010, 6, 23, 3, 43, 50); - d = RSDateWithString(@"Wed, 23 Jun 2010 03:43:50 Z"); - XCTAssertEqualObjects(d, expectedDateResult); - - expectedDateResult = dateWithValues(2010, 6, 22, 3, 57, 49); - d = RSDateWithString(@"2010-06-22T03:57:49+00:00"); - XCTAssertEqualObjects(d, expectedDateResult); - - expectedDateResult = dateWithValues(2010, 11, 17, 13, 40, 07); - d = RSDateWithString(@"2010-11-17T08:40:07-05:00"); - XCTAssertEqualObjects(d, expectedDateResult); -} - - -@end diff --git a/Frameworks/RSParser/RSParserTests/RSSInJSONParserTests.swift b/Frameworks/RSParser/RSParserTests/RSSInJSONParserTests.swift deleted file mode 100644 index 0c8b265f8..000000000 --- a/Frameworks/RSParser/RSParserTests/RSSInJSONParserTests.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// RSSInJSONParserTests.swift -// RSParser -// -// Created by Brent Simmons on 6/26/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import XCTest -import RSParser - -class RSSInJSONParserTests: XCTestCase { - - func testScriptingNewsPerformance() { - - // 0.003 sec on my 2012 iMac. - let d = parserData("ScriptingNews", "json", "http://scripting.com/") - self.measure { - let _ = try! FeedParser.parse(d) - } - } -} diff --git a/Frameworks/RSParser/RSParserTests/RSSParserTests.swift b/Frameworks/RSParser/RSParserTests/RSSParserTests.swift deleted file mode 100644 index d511c0db6..000000000 --- a/Frameworks/RSParser/RSParserTests/RSSParserTests.swift +++ /dev/null @@ -1,113 +0,0 @@ -// -// RSSParserTests.swift -// RSParser -// -// Created by Brent Simmons on 6/26/17. -// Copyright © 2017 Ranchero Software, LLC. All rights reserved. -// - -import XCTest -import RSParser - -class RSSParserTests: XCTestCase { - - func testScriptingNewsPerformance() { - - // 0.004 sec on my 2012 iMac. - let d = parserData("scriptingNews", "rss", "http://scripting.com/") - self.measure { - let _ = try! FeedParser.parse(d) - } - } - - func testKatieFloydPerformance() { - - // 0.004 sec on my 2012 iMac. - let d = parserData("KatieFloyd", "rss", "http://katiefloyd.com/") - self.measure { - let _ = try! FeedParser.parse(d) - } - } - - func testEMarleyPerformance() { - - // 0.001 sec on my 2012 iMac. - let d = parserData("EMarley", "rss", "https://medium.com/@emarley") - self.measure { - let _ = try! FeedParser.parse(d) - } - } - - func testMantonPerformance() { - - // 0.002 sec on my 2012 iMac. - let d = parserData("manton", "rss", "http://manton.org/") - self.measure { - let _ = try! FeedParser.parse(d) - } - } - - func testNatashaTheRobot() { - - let d = parserData("natasha", "xml", "https://www.natashatherobot.com/") - let parsedFeed = try! FeedParser.parse(d)! - XCTAssertEqual(parsedFeed.items.count, 10) - } - - func testTheOmniShowAttachments() { - - let d = parserData("theomnishow", "rss", "https://theomnishow.omnigroup.com/") - let parsedFeed = try! FeedParser.parse(d)! - - for article in parsedFeed.items { - XCTAssertNotNil(article.attachments) - XCTAssertEqual(article.attachments!.count, 1) - let attachment = Array(article.attachments!).first! - XCTAssertNotNil(attachment.mimeType) - XCTAssertNotNil(attachment.sizeInBytes) - XCTAssert(attachment.url.contains("cloudfront")) - XCTAssertGreaterThanOrEqual(attachment.sizeInBytes!, 22275279) - XCTAssertEqual(attachment.mimeType, "audio/mpeg") - } - } - - func testTheOmniShowUniqueIDs() { - - let d = parserData("theomnishow", "rss", "https://theomnishow.omnigroup.com/") - let parsedFeed = try! FeedParser.parse(d)! - - for article in parsedFeed.items { - XCTAssertNotNil(article.uniqueID) - XCTAssertTrue(article.uniqueID.hasPrefix("https://theomnishow.omnigroup.com/episode/")) - } - } - - func testMacworldUniqueIDs() { - - // Macworld’s feed doesn’t have guids, so they should be calculated unique IDs. - - let d = parserData("macworld", "rss", "https://www.macworld.com/") - let parsedFeed = try! FeedParser.parse(d)! - - for article in parsedFeed.items { - XCTAssertNotNil(article.uniqueID) - XCTAssertEqual(article.uniqueID.count, 32) // calculated unique IDs are MD5 hashes - } - } - - func testMacworldAuthors() { - - // Macworld uses names instead of email addresses (despite the RSS spec saying they should be email addresses). - - let d = parserData("macworld", "rss", "https://www.macworld.com/") - let parsedFeed = try! FeedParser.parse(d)! - - for article in parsedFeed.items { - - let author = article.authors!.first! - XCTAssertNil(author.emailAddress) - XCTAssertNil(author.url) - XCTAssertNotNil(author.name) - } - } -} diff --git a/Frameworks/RSParser/RSParserTests/Resources/4fsodonline.atom b/Frameworks/RSParser/RSParserTests/Resources/4fsodonline.atom deleted file mode 100644 index d715d7015..000000000 --- a/Frameworks/RSParser/RSParserTests/Resources/4fsodonline.atom +++ /dev/null @@ -1 +0,0 @@ -tag:blogger.com,1999:blog-39057418451756030232014-10-05T01:28:05.849-07:004 Fighting Serpents of DeathAnthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.comBlogger28125tag:blogger.com,1999:blog-3905741845175603023.post-4255411257634485922007-12-14T23:15:00.000-08:002007-12-15T11:35:51.128-08:004FSOD Documentary: Bloopers and StuffEven the 4 Fighting Serpents of Death don't always get it right on the first take!<br /><br /><br /><br /><br /><iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dyq9-5I_xeQrLuRdB8sMNk5uydYCcrUYCEeQM4rHZ8ittUFTmgvQzVVvwDQgWSD8DGPicg9Zx4Awulnbnqqnw' class='b-hbp-video b-uploaded' FRAMEBORDER='0' />Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com0tag:blogger.com,1999:blog-3905741845175603023.post-65523244188113060302007-12-14T00:54:00.000-08:002007-12-14T08:31:57.100-08:004FSOD The Documentary: Deleted ScenesHere are some of the scenes that didn't make the final cut,<br />and not because they weren't funny!<br /><br /><iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dzPs-XUg0wXFaUX91N90sd-kp9RGQd1tcx6Ijdw1VxRpFbgVbyrNAqmuErdrx5K_25BfMQcHVkCa2sKYMe6kA' class='b-hbp-video b-uploaded' FRAMEBORDER='0' />Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com0tag:blogger.com,1999:blog-3905741845175603023.post-48599265246991760662007-12-12T21:13:00.000-08:002007-12-12T22:18:44.752-08:00Special Feature: Jamie's PresentSynopsis:<br />Anthony has finally picked out the perfect present for his girlfriend, Jamie, tickets to an amazing concert! While Anthony sleeps though, a mysterious thief breaks into his home intended to rob him of his most valuable possessions. This thief does not realize that he has sealed his own doom by taking Jamie's Present. Upon realizing that he has been robbed, Anthony springs into action. With the help of an unlikely irish friend and a little cunningness of his own, Anthony finally tracks down Jamie's Present. The ultimate villian is a strange and dangerous Chinaman who challenges Anthony to a battle of wits, but Anthony has a couple tricks of his own up his sleeve. See how it all ends in Jamie's Present!<br /><br />Additional Information:<br />This film was produced in December of 2006 and is quite a little gem. Having completed production with only a Polaroid camera and half a roll of carbon copy paper, this film is a nice representation of just how resourceful 4FSOD Productions is. The heartfelt acting and emotionally driven storyline made this film a top seller for spouses to each other in 2006, outselling other blockbuster releases like Little Miss Sunshine, The Last Kiss, and Jackass 2.<br />A top seller, that is, among other pirated movies being sold out of trunks in neigborhood strip malls.<br /><br /><br /><iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dxg10UdzjPBh1LL62iekZ4COODDOO9owicQOz8MUenEgE5OizjKAO8bVc4At5Uvqyt-rkgdcelp3nATzkkj2w' class='b-hbp-video b-uploaded' FRAMEBORDER='0' />Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com0tag:blogger.com,1999:blog-3905741845175603023.post-17379353446679601782007-12-05T20:12:00.000-08:002007-12-05T20:58:51.202-08:00Chapter 12: Closing Credits<p>Congratulations! You have made it to the end of the documentary. Have no fear though, the saga of the 4<span class="blsp-spelling-error" id="SPELLING_ERROR_0">FSOD</span> is not even close to being over yet!</p><p> </p><p><iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dw-LyHre18Wte4cOXmZOddnlQPmEp01uEc4xP1VSAPHI20cstpqZpF7gBaXj4LeUwMNKR8TzR6umLRRJA_DpQ' class='b-hbp-video b-uploaded' FRAMEBORDER='0' /></p>Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com1tag:blogger.com,1999:blog-3905741845175603023.post-71188947587385081132007-12-04T21:07:00.001-08:002007-12-04T21:30:06.200-08:00Chapter 11: DenoumentThis is the documentary round up guys, thanks for watching! I hope you totally<br />enjoyed it!<br /><br /><iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dzQ_qyvm9VJvpfJ5LtNonHfdT72V04aBWSMzqBa9Rpkcf79kypEgCvXPTQwx31rQADaTAytCGkGWBzhdIyxbg' class='b-hbp-video b-uploaded' FRAMEBORDER='0' />Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com0tag:blogger.com,1999:blog-3905741845175603023.post-48500928774958630222007-11-28T22:10:00.001-08:002007-11-28T22:41:14.219-08:00Chapter 10: Got Smoke?Here's the infamous smoking episode where Brian is confronted<br />with his secret habit.<br /><br /><iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dzggey93fCR2yRaqAwBrghZr5mfZHOcweTASdxH-fA2ifuxvahUQLsCwUcvrfeMf1L62u5Ki_hnpIIsEtvTvw' class='b-hbp-video b-uploaded' FRAMEBORDER='0' />Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com0tag:blogger.com,1999:blog-3905741845175603023.post-69894509218073148552007-11-28T17:23:00.000-08:002007-11-28T18:29:21.621-08:00Chapter 9: Back on Track<iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dyyEcqQ9e6ZXDpY6pPLHHUlG1Vwhaw2e9LnNEJM7EOZ-MEJ1H7ZAzEALpJvG2up1T_HOlHy3w8sW0cS31Y19Q' class='b-hbp-video b-uploaded' FRAMEBORDER='0' />Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com0tag:blogger.com,1999:blog-3905741845175603023.post-67420328211917161692007-11-27T22:53:00.000-08:002007-11-27T23:29:14.452-08:00Chapter 8: Hits and MissesBack to the show, Guys!<br /><br /><iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dxC_InZ84hL0_WYLXWjltj1qNzuXrvzokChsgjGlYxO05MQYxp-PFRfKdqAigJ-t4K4C6ksxfHPwRDfDTumkQ' class='b-hbp-video b-uploaded' FRAMEBORDER='0' />Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com0tag:blogger.com,1999:blog-3905741845175603023.post-61303858976824005472007-11-23T18:53:00.000-08:002007-11-23T19:11:54.424-08:00Special: Turkey Bowl 2007<iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dz3lOptRIgVoLZcJp4JzqA9nldo43qK9HL014SysLWkFULfeW3SY3c2DK6EVbU4EbmcGWl8o1jrtfL7-1caTw' class='b-hbp-video b-uploaded' FRAMEBORDER='0' />Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com1tag:blogger.com,1999:blog-3905741845175603023.post-70942806530034860592007-11-22T15:55:00.000-08:002007-11-22T16:38:43.221-08:00Chapter 7: Alonzo The Fist(Happy Thanksgiving!)<br /><br /><iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dwD45oyzUHGPPrkVd8-r5UlB16_yWjn5DUvajwW_NpQlh8fF_110NGSsPm4xKEzaUuY6bpZ_HUDBAhs6cYaHQ' class='b-hbp-video b-uploaded' FRAMEBORDER='0' />Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com0tag:blogger.com,1999:blog-3905741845175603023.post-73119352681500966442007-11-15T07:37:00.000-08:002007-11-15T17:54:22.558-08:00Chapter 6: Separation<iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dzEjfEmgZWBfWkWHWJHRT_1RUmQiWP2ho0zboG5WDIsQhhccsQMN_cmQUrmfsWTbsVY1wCe_wXBGUjDY41Zzw' class='b-hbp-video b-uploaded' FRAMEBORDER='0' />Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com0tag:blogger.com,1999:blog-3905741845175603023.post-61115399604136142172007-11-13T00:06:00.000-08:002007-11-15T07:37:10.404-08:00Chapter 5: What's in a Name?<iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dyALG7yoDxJ5loRqrKOI6EUgjnMxxEkX0jtkmi1jf_NGQdSpG4fdC7P6NmM_2rK3JRa4T8Xs03rkYc7A3DALg' class='b-hbp-video b-uploaded' FRAMEBORDER='0' />Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com0tag:blogger.com,1999:blog-3905741845175603023.post-79443865606624753742007-11-01T17:04:00.000-07:002007-11-27T10:33:07.319-08:00Chapter 4: Success!<iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dz_-zcPj94qXbSRfG8cY-c8uDfYBGiCJ4o3pwOvd2WKl-yLHJxAnCY543QAl6sNTsQxfRg14ogRbdgvZjtLPg' class='b-hbp-video b-uploaded' FRAMEBORDER='0' />Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com0tag:blogger.com,1999:blog-3905741845175603023.post-88366528659469257432007-10-31T21:00:00.000-07:002007-10-31T21:25:51.554-07:00Documetary: Humble Beginings<p>Chapter Three, where the idea all started.</p><p><iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dz3YGwcLrT0lZhtrQQKaQF1leYvcpcJr-lVdYCaTFoMjAHQtwAjc7vp9cE4HViAWIJtHv32cps4AGnpsx-M7Q' class='b-hbp-video b-uploaded' FRAMEBORDER='0' /></p>Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com0tag:blogger.com,1999:blog-3905741845175603023.post-34910670706831593692007-10-30T17:40:00.000-07:002007-10-30T17:54:09.994-07:00Documentary: The Four<p>This is the second chapter of the documentary. Here, we introduce the four actors who portray the 4 Fighting Serpents of Death, and little more about who they really are. </p><p><iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dwZskTs6WHZnO-dJAY3nCnSE51EPhr-R69YKEe8mzfAvtCqGStq9yYFtr6bbGxZ2Ffu0PFidYQge08vqG4Xlw' class='b-hbp-video b-uploaded' FRAMEBORDER='0' /></p>Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com0tag:blogger.com,1999:blog-3905741845175603023.post-33190610334281896542007-10-26T10:45:00.000-07:002007-10-26T12:35:32.562-07:004FSOD: The Documentary - Opening Credits<p>Here it is folks, the official 4 Fighting Serpents of Death Documentary. What you are seeing now are the opening credits. Check back in weekly as we will be posting subsequent chapters. 12 in all I believe!</p><p> </p><p><iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dwaMaRADWrYCcNuaVJ4PUHWyoeoLnc21ohJRcUbg7iOQUtgHNFTEzzphTjpwJMWygE9o1cQNOFGL0pQh9bM5Q' class='b-hbp-video b-uploaded' FRAMEBORDER='0' /></p>Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com1tag:blogger.com,1999:blog-3905741845175603023.post-7114995983053335962007-10-21T20:22:00.000-07:002007-10-21T22:33:09.329-07:004FSOD: The Documentary!In the footsteps of VH1 Behind the Music, E! True Hollywood Story, and Spinal Tap, the 4 Fighting Serpents of Death have collaborated with Oregon Public Access Television to release their own behind-the-scenes documentary. This complete segment features candid interviews from all of the Serpents as well as anyone ever involved with their films. We will be posting this documentary up in segments, once a week for the next 3 months. It is exciting I know. But before we get started, let's see some trailers for the Documentary!<br /><br /><br /><br /><p><iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dzcR92LiAsXqsOFoG-hjUEen2MLTqNqXNbeNUATPebQLZ1yKQ5dsI-BRbFehbYYFuD-G8kx-B75mD9w02bKhg' class='b-hbp-video b-uploaded' FRAMEBORDER='0' /></p><p> </p><p> </p><iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dygJfitUZmDjWaPuCe1iyEGueFWRSVQtjfEUD2w-NjGXdPdBzlk-GytF3VSg1rYcAqeTjR2zSPxHRRFGq38ow' class='b-hbp-video b-uploaded' FRAMEBORDER='0' />Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com0tag:blogger.com,1999:blog-3905741845175603023.post-13676006727225311882007-10-21T00:06:00.000-07:002007-10-21T00:43:00.495-07:004FSOD: Action Impending!<p>In 2001, the 4FSOD released "Action Impending". You might notice that the acronym for the film is A.I., this was no accident. Knowing that they're popularity had taken a downturn, the Serpents hoped to show up on internet searches by having the same acronym as Stephen Spielberg's film "A.I." which was wildly successful that year. To a lesser degree, the Serpents wanted to show up in Yahoo searches for Allen "The Answer" Iverson too, hoping to attract the younger male demographic. Really, there was no coherent plot to this film. Dick Johnson is strolling with his girlfriend through a parking garage when they happen to come across a battle between Lee Bang (formerly known as 100% BEEF) and Wang Chung and thier nemesis, Alonzo the Fist. Dick Johnson's girl is killed by a flying bamboo stick and he vows revenge. The Serpents now have one more ally in their campaign to take down Alonzo!</p><p><iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dzyGsZvqadWDiHuePjXi1heKyYyyPti7puxO64WdRTvfsDLyNzjsZefN91kpz6t2ezJFggcqbnhL4kjVh_Vaw' class='b-hbp-video b-uploaded' FRAMEBORDER='0' /></p>Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com0tag:blogger.com,1999:blog-3905741845175603023.post-37763903805929167462007-10-12T17:26:00.000-07:002007-10-12T17:55:56.776-07:004FSOD: Critical Review<iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dz-uqLhP2VHS0eK5WJMLHAW4pxevce2gBNo_KusJViQfGeEvRJfNh9UG5Uk4xtLduZC6UeEpxmze7CKKCjYig' class='b-hbp-video b-uploaded' FRAMEBORDER='0' />Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com0tag:blogger.com,1999:blog-3905741845175603023.post-60072994970418036702007-10-03T08:59:00.000-07:002007-10-03T09:22:49.587-07:004FSOD: Deadly WatersDeadly Waters was not released in theaters. Rather, it was released as a Made-For-DVD special. Wang Chung comments, "It would have cost us way too much to produce this one for the big screen. Also, the action we do have looks <span class="blsp-spelling-error" id="SPELLING_ERROR_0">alot</span> more impressive when it's crammed into the small screen." After the non-success of <span class="blsp-spelling-error" id="SPELLING_ERROR_1">Paresuture</span> the year before, many endorsers backed out of the 4<span class="blsp-spelling-error" id="SPELLING_ERROR_2">FSOD</span> campaign, leaving their finances dwindled. Deadly Waters was their next attempt to get back into the spotlight.<br /><br />Plot Synopsis:<br />Wang Chung decides to go to the Oregon Coast for a weekend getaway. All the other Serpents are busy with jobs and other commitments so he calls up his old college <span class="blsp-spelling-error" id="SPELLING_ERROR_3">roomate</span>, Kevin. While kicking it at <span class="blsp-spelling-error" id="SPELLING_ERROR_4">Rockaway</span> Beach, Kevin is ambushed by none other than Alonzo the Fist! This time it's personal! Alonzo also steals Kevin's rented Chevy Cavalier. Wang Chung manages to hunt Alonzo down to the rocky beach. With nowhere else for either of them to go, they must now fight.<br /><br /><br /><p><iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dySRctwDWWPjKF_lYL5Ou0G2ApDJd0FHy8817hhc_FPha9EknOgitHUOFMLSLpBqtFssQgbZlmMGL6BzJDnow' class='b-hbp-video b-uploaded' FRAMEBORDER='0' /></p><p></p><p>*4<span class="blsp-spelling-error" id="SPELLING_ERROR_5">FSOD</span>*</p>Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com0tag:blogger.com,1999:blog-3905741845175603023.post-84560047708297999962007-10-01T21:55:00.000-07:002007-10-01T22:20:14.881-07:004FSOD: Search for AlonzoThis emotionally charged installment of the 4FSOD is sure to be a rollercoaster. We now reveal some of Alonzo's past. Alonzo the Fist was not always a bad dude. He once had a life, he once had a family, and he once had a sister. Her name was Sophia Marteaux, the "Soft Hammer". Some scholars suspect that Alonzo's trip down evil lane began when he was kidnapped by the government and forced to be the guinea pig for a number of human weapon experiments. They thought no one would notice Alonzo missing, but they were wrong. Sophia Marteaux noticed, and now she wants to get her brother back.<br /><br />The Search for Alonzo<br /><p><iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dwFYsxOIN76KChzyh46wWrb9kAUVYkXM6s6kiZhjleKZ_Yd7pE1Y2iIyo7cYPik1gQ7VBZPsRQJN-CJU7c5nw' class='b-hbp-video b-uploaded' FRAMEBORDER='0' /></p><p> </p><p>*4FSOD*</p>Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com0tag:blogger.com,1999:blog-3905741845175603023.post-29862062712274548652007-10-01T14:10:00.001-07:002007-10-01T14:26:15.951-07:004FSOD: ParesutureThe film "Paresuture" was probably the Serpent's least successful film. While filled with promise initially, box office numbers proved that the film was a flop. Wang Chung defends, "You know, it was actually a little abstract compared to the previous films. But we wanted to keep things fresh, that's why we did it. Ya live ya learn I guess."<br />In Paresuture, Alonzo the Fist purchases a time machine through Craigslist and intends to go the future to learn the next 10 superbowl winners. Then return to the present and bet on them. Wang Chung and his partner, 100% Beef buy a time-machine of their own but use the wrong crystals! Consequently, they get catapulted into the past. It's one big chronological mess that needs to be sorted out, or else!<br />The Past, The Present, and The Future, blur together to make... Paresuture!<br /><br /><br /><p><iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dwISi0-BLlWw2AXM0_FWBiv2JYtgKqTXM0caqQ2kE7Vklww5W409kgfMtmzsrlGM2wmenFMjcvuajGsXaIDlQ' class='b-hbp-video b-uploaded' FRAMEBORDER='0' /></p><p>*4FSOD*</p>Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com0tag:blogger.com,1999:blog-3905741845175603023.post-83939852294338951462007-09-30T16:39:00.000-07:002007-09-30T20:00:48.192-07:004FSOD: The Nemesis and The ConfrontationAlonzo The Fist:<br />Every hero needs a villian, like every rose has it's thorn. In general, the 4FSOD clean up on most of the competition. But then they met Alonzo The Fist. No one really knows where he came from and his lineage is the stuff of scary campfire stories. One thing is for certain though, Alonzo The Fist really hates the Four Fighting Serpents of Death.<br /><br /><iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dz0Rove_XTtpCbTLlPqCxYWklr4JyMLWqeasU5E7KWU1I-r70TH8CYdCSG1QLPllrlTeH8PUaiwkLiz9jFdtg' class='b-hbp-video b-uploaded' FRAMEBORDER='0' /><br /><br /><br /><br /><br /><p> </p><p></p><p>*4FSoD*</p>Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com0tag:blogger.com,1999:blog-3905741845175603023.post-62592224328992184292007-09-30T16:00:00.000-07:002007-09-30T20:25:34.450-07:004FSOD: Vengence CompoundVengence Compound:<br />In this movie right here, we get to see one of the most intense fight sequences ever caught tape. Alonzo The Fist has managed to captured three of the four Serpents and locked them in a room that blasts nothing but Trio's hit single, "Da Da Da" on repeat. A horrible torture indeed. Alonzo plans to finish of the extermination and hunts down the remaining Serpent, Wang Chung who was picking apples in a nearby orchard at the time. But it wasn't going to be that easy. Being a resilient tiger of action, Wang Chung battles hard, not only to save his own life but the lives of his friends! Could this be 4FSOD's last stand? Watch and find out!<br /><br /><br /><iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dx9OzVrg6E1GmsZH3bzwai_uPqwDKDz6-i43epWK4eUg5B0rSVgdAuFYOQtRKBA1pC5bRpyAOGbJdCTpeWflg' class='b-hbp-video b-uploaded' FRAMEBORDER='0' />Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com0tag:blogger.com,1999:blog-3905741845175603023.post-82352286232836488352007-09-28T10:38:00.001-07:002007-09-28T10:56:18.792-07:004FSOD: Who is Stir Frey Jones?Who are the four fighting serpents? Are there really 4 of them? We'll answer that later. For now, you only need to know of one Serpent of Death and his name is Stir Frey Jones. In this action packed film, a couple hoodlums steal a woman's purse in a parking garage. The nerve!<br />Little did they now, a Serpent was watching in wings. Exhibiting a merciless tenacity against evil, Stir Frey Jones tears things up all in the name of justice. The only question now is who will spare mercy on these bad guys?<br /><br /><br /><p><iframe allowFullScreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dxRJJzvi9UBzAIbN6yiv3-7O1NWvXGQ6mtedeJwfe-G44tldl5YtLtl7tomHMFPvy9xu4Hn3wkuTnU7wXLTqw' class='b-hbp-video b-uploaded' FRAMEBORDER='0' /></p><p> </p><p>*4FSoD*</p>Anthonyhttp://www.blogger.com/profile/05977683271610009040noreply@blogger.com0 \ No newline at end of file diff --git a/Frameworks/RSParser/RSParserTests/Resources/DaringFireball.atom b/Frameworks/RSParser/RSParserTests/Resources/DaringFireball.atom deleted file mode 100644 index 4d84dadbd..000000000 --- a/Frameworks/RSParser/RSParserTests/Resources/DaringFireball.atom +++ /dev/null @@ -1,1821 +0,0 @@ - - -Daring Fireball -By John Gruber - - -https://daringfireball.net/feeds/main - - -2017-06-27T00:54:20ZCopyright © 2017, John Gruber - The Talk Show: ‘I Do Like Throwing a Baby’ - - - - tag:daringfireball.net,2017:/linked//6.33853 - 2017-06-27T00:54:17Z - 2017-06-27T00:54:20Z - - John Gruber - http://daringfireball.net/ - - New episode of America’s favorite 3-star podcast, with special guest John Moltz. Topics include more follow-up from WWDC 2017, the iPad Pro models and ProMotion, Scott Forstall’s interview with John Markoff regarding the 10-year anniversary of the original iPhone, the ongoing shitshow at Uber, quick thoughts on the Nintendo Switch, and more. Also: guess which John enjoys throwing babies into the air.

- -

Sponsored by:

- -
    -
  • Squarespace: Make your next move. Use code GRUBER for 10% off your first order.
  • -
  • Away: High-quality luggage with built-in USB chargers. Save $20 with promo code TALKSHOW.
  • -
  • MailRoute: Hosted spam and virus protection for email. Use this link for 10% off for the life of your account.
  • -
- - - - ]]> - - Virgin Mobile Partners With Apple to Go iPhone-Only With $1 Service - - - - tag:daringfireball.net,2017:/linked//6.33852 - 2017-06-26T23:51:11Z - 2017-06-26T23:51:13Z - - John Gruber - http://daringfireball.net/ - - Josh Centers, writing for TidBITS on Virgin Mobile’s intriguing decision to go iPhone-only:

- -
-

Pundits have long suspected that two roadblocks stood in the way -of Apple becoming a carrier: the infrastructure is incredibly -expensive, even if you lease it from the larger carriers, and -Apple could limit the iPhone business if it were to compete with -the major carriers.

- -

But Apple has sidestepped those concerns by essentially taking -over a carrier (actually a carrier-owned MVNO — Mobile Virtual -Network Operator) without acquiring it. Apple may not own Virgin -Mobile, but Virgin Mobile is now utterly dependent on Apple and -will benefit through promotion in Apple Stores.

- -

We shouldn’t read too much into this deal, but at the very least -it’s unusual to see a company like Virgin Mobile going all-in on -the iPhone. And it might point toward Apple dipping its toe into -the MVNO business.

-
- -

Virgin Mobile is owned by Sprint (and thus uses Sprint’s back-end), and in my experience Sprint is the worst of the U.S. carriers, so this is not a panacea. But it is intriguing.

- - - - ]]>
-
- The Verge: ‘Apple’s AR Is Closer to Reality Than Google’s’ - - - - tag:daringfireball.net,2017:/linked//6.33850 - 2017-06-26T23:02:21Z - 2017-06-26T23:02:23Z - - John Gruber - http://daringfireball.net/ - - Two great examples via the very fun Made With ARKit Twitter account: here and here.

- - - - ]]>
-
- Rene Ritchie’s First Look at the iOS 11 Public Beta - - - - tag:daringfireball.net,2017:/linked//6.33849 - 2017-06-26T22:55:13Z - 2017-06-26T23:13:17Z - - John Gruber - http://daringfireball.net/ - - Rene Ritchie has a comprehensive look at the just-released public beta of iOS 11. Romain Dillet has a good preview at TechCrunch too. The gist of both previews: it’s the “I hope Apple truly focuses on the iPad this year” release of iOS that we’ve been waiting for.

- -

I’ve been using the developer betas on my 10.5-inch iPad Pro review unit and a spare iPhone. I’m willing to wait to install iOS 11 on my primary iPhone, but at this point, bugs be damned, I wouldn’t want to use an iPad running iOS 10.3. It’s stable enough, and the benefits of the great new features for iPad far outweigh the downsides of the beta (which, in addition to crashing bugs, include questionable battery life).

- - - - ]]>
-
- Amazon’s New Echo Show Is Very Cool and a Little Creepy - - - - tag:daringfireball.net,2017:/linked//6.33848 - 2017-06-26T22:08:09Z - 2017-06-26T23:18:49Z - - John Gruber - http://daringfireball.net/ - - Mat Honan, writing for BuzzFeed:

- -
-

It has this wild new feature called Drop In. Drop In lets you give -people permission to automatically connect with your device. -Here’s how it works. Let’s say my father has activated Drop In for -me on his Echo Show. All I have to do is say, “Alexa, drop in on -Dad.” It then turns on the microphone and camera on my father’s -device and starts broadcasting that to me. For the several seconds -of the call, my father’s video screen would appear fogged over. -But then there he’ll be. And to be clear: This happens even if he -doesn’t answer. Unless he declines the call, audibly or by tapping -on the screen, it goes through. It just starts. Hello, you look -nice today.

- -

Honestly, I haven’t figured out what to think about this yet. But, -it’s here.

-
- -

I know what to think of this: No fucking way do I want this.

- -

Update: I’ve already gotten a few reader responses arguing that this feature could be great for an Echo Show in the home of an elderly relative. You visit and set it up in their house, explain to them what it does, and then you can check in with them without their needing to do anything at all. I can see that. You can think of it as the digital equivalent of having a set of keys to someone’s house — something you’d only grant to a deeply trusted friend or loved one.

- - - - ]]>
-
- Verizon to Block Email Addresses From Rival Carriers From Logging Into Yahoo or Tumblr Accounts - - - - tag:daringfireball.net,2017:/linked//6.33847 - 2017-06-26T21:18:46Z - 2017-06-26T22:18:03Z - - John Gruber - http://daringfireball.net/ - - From a Tumblr help document euphemistically titled “Heads-Up for AT&T Customers”:

- -
-

Starting on June 30, 2017, att.net customers will no longer be -able to log in to their Yahoo and Tumblr accounts through email -addresses with the following domains: att.net, ameritech.net, -bellsouth.net, flash.net, nvbell.net, pacbell.net, prodigy.net, -sbcglobal.net, snet.net, swbell.net, and wans.net.

-
- -

The sheer egregiousness of this is outrageous on its face, but it’s even worse when you consider that Tumblr, when it was independent, was a champion for net neutrality.

- -

Update: TechCrunch says it’s just a deal expiring, not spite:

- -
-

As part of the new corporate merger of Yahoo and Aol under the -Oath brand, it looks like Yahoo accounts will no longer be -accessible through AT&T email addresses (or those of any A&T -subsidiaries).

- -

The move provoked some uproar among net neutrality advocates, but -it seems to be less about creating walled gardens and more about -cleaning up prior commitments and pre-existing partnerships. -While there is a level of inconvenience for AT&T customers, this -is less about net neutrality and more about unwinding those -corporate deals.

-
- -

I still say fuck Verizon and their stance on net neutrality.

- - - - ]]>
-
- Trump’s Lies, the Definitive List - - - - tag:daringfireball.net,2017:/linked//6.33846 - 2017-06-26T21:08:43Z - 2017-06-26T21:08:44Z - - John Gruber - http://daringfireball.net/ - - Copiously documented and perfectly presented. Looked striking in the print edition, too.

- - - - ]]>
-
- - - - - tag:daringfireball.net,2017:/feeds/sponsors//11.33851 - Daring Fireball Department of Commerce - 2017-06-26T19:22:44-04:00 - 2017-06-26T19:22:46-04:00 - Time is your most precious resource. You need to know how you are spending it.
-But time tracking sucks. Big Time. (Pun intended.)

- -

The brand new Timing fixes that. It automatically tracks which apps, documents and websites you use — without start/stop timers. See how you spend your time, eliminate distracting activities, and improve your client billing. Mind you, this data is super sensitive, so Timing keeps it safe on your Mac.

- -

Stop worrying about time and focus on doing your best work instead.

- -

Download a free 14-day trial today and get 10 percent off through next Monday.

- - - - ]]>
- [Sponsor] Timing — Automatic Time Tracking for Mac
- Delta Updates and App Thinning Do Not Solve the Apps-Are-Too-Damn-Big Problem - - - - tag:daringfireball.net,2017:/linked//6.33845 - 2017-06-25T15:31:10Z - 2017-06-25T15:31:12Z - - John Gruber - http://daringfireball.net/ - - Matt Birchler:

- -
-

“App thinning” is not a magic bullet that erases this problem -though, as Facebook Messenger, which shows as being 154 MB, still -downloaded 99 MB of data for its update. […]

- -

So are giant app sizes a problem? Yes. Do delta updates allow -these updates to use less data? Yes. Do delta updates make these -large apps a non-issue? Hell no!

-
- - - - ]]>
-
- Mnml – A Mac Client for Medium - - - - tag:daringfireball.net,2017:/linked//6.33844 - 2017-06-25T00:14:25Z - 2017-06-25T02:42:27Z - - John Gruber - http://daringfireball.net/ - - My thanks to Mnml for sponsoring this week’s DF RSS feed. It’s a native Mac app client for Medium, and can be used for writing, blogging, and notetaking. Based upon the same engine that twice won Desk “Best Apps of the Year” honors, Mnml has all the features you’ll need, wrapped in an attractive, fun – and yes, minimal – interface. Anyone who writes for Medium and prefers native client apps should check it out.

- - - - ]]>
-
- Scott Galloway on Amazon and Whole Foods - - - - tag:daringfireball.net,2017:/linked//6.33843 - 2017-06-23T18:45:50Z - 2017-06-23T19:23:34Z - - John Gruber - http://daringfireball.net/ - - Scott Galloway:

- -
-

Amazon / Whole Foods will be the fourth-largest grocer in the US, -and will likely post growth rates no $10B+ retailer, sans Amazon, -has registered. The Seattle firm will apply its operational chops -and lower (zero) profit hurdle to the Whole Foods business model -and bring prices (way) down. If you wish you could shop at Whole -Foods more often, but it’s too expensive, your prayers have been -answered. Whole Foods will become the grocery equivalent of a -Mercedes for the price of a Toyota. Grocery has stuck their chin -out (little innovation), and the entire sector is about to have -its jaw shattered.

-
- -

It’s a great piece. I disagree with him on this though:

- -
-

Amazon will displace Apple as the top tech hardware innovator, -with Alexa cementing itself as the gadget that defines the decade -(post iPhone). Grocery / commerce via Alexa will create the -utility that Alexa needs to [maintain its lead] over Google and -Apple’s home / voice offerings as they try to play catch-up.

-
- -

Alexa may well maintain its lead in the smart speaker market. It may even grow. Maybe HomePod will be a complete bust. But even if all of that happens, the smartphone will remain the dominant device in people’s lives. Something will eventually replace the phone, but smart speakers aren’t it.

- -

Hardware just isn’t where Amazon is good.

- - - - ]]>
-
- Google Will Stop Reading Your Emails for Gmail Ads - - - - tag:daringfireball.net,2017:/linked//6.33842 - 2017-06-23T18:32:03Z - 2017-06-23T18:32:05Z - - John Gruber - http://daringfireball.net/ - - Mark Bergen, reporting for Bloomberg:

- -
-

Google is stopping one of the most controversial advertising -formats: ads inside Gmail that scan users’ email contents. The -decision didn’t come from Google’s ad team, but from its cloud -unit, which is angling to sign up more corporate customers.

- -

Alphabet Inc.’s Google Cloud sells a package of office software, -called G Suite, that competes with market leader Microsoft Corp. -Paying Gmail users never received the email-scanning ads like the -free version of the program, but some business customers were -confused by the distinction and its privacy implications, said -Diane Greene, Google’s senior vice president of cloud. “What we’re -going to do is make it unambiguous,” she said.

-
- -

This is terrific news. Not just because it’s a good policy change in and of itself, but I take it as a sign that Google’s leadership is starting to realize how much damage they’ve done to the company’s reputation by playing fast and loose with their users’ privacy.

- - - - ]]>
-
- Who Americans Spend Their Time With - - - - tag:daringfireball.net,2017:/linked//6.33841 - 2017-06-23T18:13:55Z - 2017-06-23T18:15:59Z - - John Gruber - http://daringfireball.net/ - - Via Jim Coudal, who summarizes this perfectly: “Poetry, in data”.

- - - - ]]>
-
- Kevin Drum’s Thoughtful Critique of Trumpcare - - - - tag:daringfireball.net,2017:/linked//6.33840 - 2017-06-23T17:58:25Z - 2017-06-23T17:58:27Z - - John Gruber - http://daringfireball.net/ - - A succinct rundown of what’s wrong with the Senate Republicans’ “health care” bill.

- - - - ]]>
-
- Nintendo: Switch Shortages Are ‘Definitely Not Intentional’ - - - - tag:daringfireball.net,2017:/linked//6.33839 - 2017-06-23T17:46:13Z - 2017-06-23T17:46:15Z - - John Gruber - http://daringfireball.net/ - - Kyle Orland, reporting for Ars Technica:

- -
-

Since the days of the NES, people have accused Nintendo of -intentionally underproducing hardware in order to drive an -artificial feeding frenzy of demand in the marketplace. With the -Nintendo Switch remaining nearly impossible to find at retailers -nationwide, those same accusations of “false scarcity” have been -bubbling up in certain corners.

- -

Nintendo Senior Director of Corporate Communications Charlie -Scibetta wants to push back on those accusations. “It’s definitely -not intentional in terms of shorting the market,” he told Ars in a -recent interview. “We’re making it as fast as we can. We want to -get as many units out as we can to support all the software that’s -coming out right now… our job really is to get it out as quick -as we can, especially for this holiday because we want to have -units on shelves to support Super Mario Odyssey.”

-
- - - - ]]>
-
- How to Build Smaller Apps - - - - tag:daringfireball.net,2017:/linked//6.33838 - 2017-06-22T23:03:38Z - 2017-06-22T23:03:40Z - - John Gruber - http://daringfireball.net/ - - Ben Sandofsky:

- -
-

Popular social networking apps are over 400 megs. With weekly -releases, over one year you’ll download twenty gigs of data.

- -

Since we launched Halide, the most unexpected compliment we’ve -heard is about its size. At 11 megs, we’ll push less data in one -year than a social network pushes in a single update.

- -

“So you aren’t using Swift,” asked a friend. After all, Swift -bundles its standard libraries into your app, bloating its size. -Halide is almost entirely Swift. How did we do it? Let’s start -with the technical bits.

-
- -

His conclusion is spot-on:

- -
-

There really is one weird trick to lose size: focus on your customers.

-
- - - - ]]>
-
- Bad App Citizens - - - - tag:daringfireball.net,2017:/linked//6.33837 - 2017-06-22T22:51:51Z - 2017-06-23T16:54:33Z - - John Gruber - http://daringfireball.net/ - - Jon Darke:

- -
-

This got me thinking — as a user who has a lot of apps -installed, how much bandwidth does my phone use to keep my apps -updated? […]

- -

One Friday I turned off auto-update for apps and let the update -queue build up for a week. The results shocked me.

-
- -

It’s getting to the point where most apps can’t be updated over cellular because they’re all over 100 MB. This is madness.

- -

Update: Many readers have written to argue that the listed sizes in the App Store aren’t what you actually download when updating an app, thanks to app thinning and other features. OK, but even with app thinning and delta updates these apps are still way too big as downloads and take up way too much storage on devices.

- - - - ]]>
-
- More Than 1,000 Uber Employees Ask for Travis Kalanick to Return - - - - tag:daringfireball.net,2017:/linked//6.33836 - 2017-06-22T22:24:24Z - 2017-06-22T22:24:26Z - - John Gruber - http://daringfireball.net/ - - Dan Primack, reporting for Axios:

- -
-

More than one thousand current Uber employees have signed a letter -to the company’s board of directors, asking for the return of -deposed CEO Travis Kalanick “in an operational role.” One of its -venture capital investors also is chiming in, with a similar -message.

-
- -

Not surprising to me at all — Uber was made in Kalanick’s image.

- - - - ]]>
-
- Kara Swisher: ‘Susan Fowler Proved That One Person Can Make a Difference’ - - - - tag:daringfireball.net,2017:/linked//6.33835 - 2017-06-22T20:15:16Z - 2017-06-22T20:15:18Z - - John Gruber - http://daringfireball.net/ - - Kara Swisher:

- -
-

It was Lao Tzu who said that “the journey of a thousand miles -begins with a single step.”

- -

In the case of complete and utter change reeling through Uber -right now — culminating in the resignation of its once -untouchable CEO Travis Kalanick — it turns out that it began with -one of the most epic blog posts to be written about what happens -when a hot company becomes hostage to its increasingly -dysfunctional and toxic behaviors.

- -

It was clear from the moment you read the 3,000-word post by -former engineer Susan Fowler about her time at the car-hailing -company that nothing was going to be the same. Titled simply, -“Reflecting on one very, very strange year at Uber,” the -essay deftly and surgically laid out the map that the media and -others would use to prove to its out-to-lunch board and waffling -investors that Uber CEO Travis Kalanick had to go.

-
- -

The truth and courage are a powerful combination.

- - - - ]]>
-
- Gizmodo Investigation Exposes Websites Collecting Form Data Before You Hit ‘Submit’ - - - - tag:daringfireball.net,2017:/linked//6.33834 - 2017-06-22T19:54:12Z - 2017-06-22T23:18:05Z - - John Gruber - http://daringfireball.net/ - - Great investigative work by Kashmir Hill and Surya Mattu for Gizmodo:

- -
-

During a recent investigation into how a drug-trial recruitment -company called Acurian Health tracks down people who look online -for information about their medical conditions, we discovered -NaviStone’s code on sites run by Acurian, Quicken Loans, a -continuing education center, a clothing store for plus-sized -women, and a host of other retailers. Using Javascript, those -sites were transmitting information from people as soon as they -typed or auto-filled it into an online form. That way, the company -would have it even if those people immediately changed their minds -and closed the page. […]

- -

We decided to test how the code works by pretending to shop on -sites that use it and then browsing away without finalizing the -purchase. Three sites — hardware site Rockler.com, gift site -CollectionsEtc.com, and clothing site BostonProper.com — sent us -emails about items we’d left in our shopping carts using the email -addresses we’d typed onto the site but had not formally submitted. -Although Gizmodo was able to see the email address information -being sent to Navistone, the company said that it was not -responsible for those emails.

-
- -

They weren’t responsible for sending the emails, but they were responsible for the email addresses being sent to those websites in the first place. Sending form data surreptitiously is morally wrong, and everyone knows it.

- -

This might sound hyperbolic, but I mean it: I think we’d be better off if JavaScript had never been added to web browsers.

- - - - ]]>
-
- Uber’s Biggest Problem: Its Business Model - - - - tag:daringfireball.net,2017:/linked//6.33833 - 2017-06-22T18:54:45Z - 2017-06-22T18:54:46Z - - John Gruber - http://daringfireball.net/ - - Christopher Mims, in his column for The Wall Street Journal:

- -
-

But even when it steers through that thicket of crises, Uber will -have to come to grips with a fundamental vulnerability that is -increasingly apparent in the company’s business model. Uber may be -great at technology, but unlike the businesses of Google, -Facebook, Apple or Amazon, technology hasn’t proven to be a -significant barrier to new entrants in ride-sharing. Across the -globe, Uber has dozens of competitors, and in many markets they -have grabbed the lion’s share of the ride-sharing market.

- -

Even if Uber fixes all of its current problems, it’s increasingly -unlikely that it can live up to the inflated expectations that -come with the nearly $70 billion valuation that have made it the -world’s most valuable startup. There are barbarians at Uber’s -gate, and it’s sorely in need of a moat.

-
- -

This is why they’re pursuing self-driving technology so aggressively. There’s simply no way that Uber is worth $70 billion without some sort of exclusive technical advantage. That’s the interesting flip side to Kalanick’s ouster — I’m not sure who would want the job.

- - - - ]]>
-
- Chris Lattner on His Stint at Tesla - - - - tag:daringfireball.net,2017:/linked//6.33832 - 2017-06-22T18:26:01Z - 2017-06-22T19:13:09Z - - John Gruber - http://daringfireball.net/ - - Chris Lattner has updated his resume with his accomplishments at Tesla. Unsurprisingly, it sounds like he got a lot done in just five months — including, ironically, addressing an engineering talent retention problem.

- - - - ]]>
-
- Inside Travis Kalanick’s Resignation - - - - tag:daringfireball.net,2017:/linked//6.33831 - 2017-06-22T18:07:14Z - 2017-06-22T18:07:15Z - - John Gruber - http://daringfireball.net/ - - Mike Isaac, reporting for The New York Times:

- -
-

Travis Kalanick’s final hours as Uber’s chief executive played out -in a private room in a downtown Chicago hotel on Tuesday.

- -

There, Mr. Kalanick, who was on a trip to interview executive -candidates for Uber, was paid a surprise visit. Two venture -capitalists — Matt Cohler and Peter Fenton of the Silicon Valley -firm Benchmark, which is one of Uber’s biggest shareholders — -presented Mr. Kalanick with a list of demands, including his -resignation before the end of the day. The letter was from five of -Uber’s major investors, including Benchmark and the mutual fund -giant Fidelity Investments. […]

- -

By the end of the day, after hours of haggling and arguing, that -course was clear: Mr. Kalanick agreed to step down as Uber’s chief -executive.

-
- -

Truly great reporting from Isaac, including the fact that even during his brief “leave of absence”, he wasn’t really absent at all:

- -
-

In reality, Mr. Kalanick had little intention of staying away from -his company. Almost immediately after announcing the leave of -absence, he worked the phones to push out Mr. Bonderman for making -the sexist comment onstage at an Uber employee meeting. With the -two increasingly at odds, Mr. Kalanick sent out a flurry of texts, -phone calls and emails to his allies to pressure Mr. Bonderman to -step down from Uber’s board. Hours later, Mr. Bonderman did.

-
- - - - ]]>
-
- Narrative Maps for ‘Choose Your Own Adventure’ Books - - - - tag:daringfireball.net,2017:/linked//6.33830 - 2017-06-22T17:50:49Z - 2017-06-22T17:59:41Z - - John Gruber - http://daringfireball.net/ - - Sarah Laskow, writing for Atlas Obscura:

- -
-

The last installment of the original “Choose Your Own Adventure” -series came out in 1998, but since 2004, Chooseco, founded by -one of the series’ original authors, R.A. Montgomery, has been -republishing classic volumes, as well as new riffs on the form of -interactive fiction that seemed ubiquitous in the 1980s and ’90s. -The new editions also carry an additional feature — maps of the -hidden structure of each book.

-
- -

Just looking at the maps included in the article, it’s interesting how wildly varying in complexity these stories were. See also: Christian Swinehart’s color-coded graphical representations of these books.

- -

(Via Kottke.)

- - - - ]]>
-
- John Markoff’s Interview With Scott Forstall and Members of the Original iPhone Team - - - - tag:daringfireball.net,2017:/linked//6.33829 - 2017-06-22T17:45:20Z - 2017-06-22T20:17:34Z - - John Gruber - http://daringfireball.net/ - - The Computer History Museum (now on YouTube):

- -
-

Museum Historian John Markoff moderates a discussion with former -iPhone team members Hugo Fiennes, Nitin Ganatra and Scott Herz, -followed by a conversation with Scott Forstall.

-
- -

Fascinating stories.

- -

Forstall was great. It’s hard to believe he’s been out of Apple and out of the limelight for 5 years — watching him on stage with Markoff it feels like he never left.

- - - - ]]>
-
- ‘Four Things in Those Two Sentences’ - - - - tag:daringfireball.net,2017:/linked//6.33828 - 2017-06-21T16:55:23Z - 2017-06-21T16:55:26Z - - John Gruber - http://daringfireball.net/ - - Kara Swisher on Travis Kalanick:

- -
-

Uber confirmed the resignation, and the company’s board issued a -statement that said, in part: “Travis has always put Uber first. -This is a bold decision and a sign of his devotion and love for -Uber.” (For those who don’t speak fluent tech director, there are -four things in those two sentences that are not true.)

-
- - - - ]]>
-
- Uber Founder Travis Kalanick Resigns as C.E.O. - - - - tag:daringfireball.net,2017:/linked//6.33827 - 2017-06-21T16:53:04Z - 2017-06-21T16:53:06Z - - John Gruber - http://daringfireball.net/ - - Mike Isaac, reporting for The New York Times:

- -
-

In the letter, titled “Moving Uber Forward” and obtained by The -New York Times, the investors wrote to Mr. Kalanick that he must -immediately leave and that the company needed a change in -leadership. Mr. Kalanick, 40, consulted with at least one Uber -board member, and after long discussions with some of the -investors, he agreed to step down. He will remain on Uber’s board -of directors.

- -

“I love Uber more than anything in the world and at this difficult -moment in my personal life I have accepted the investors request -to step aside so that Uber can go back to building rather than be -distracted with another fight,” Mr. Kalanick said in a statement.

-
- -

From the outside, it seems like this was inevitable. It was only a question of when.

- - - - ]]>
-
- Chris Lattner Out at Tesla - - - - tag:daringfireball.net,2017:/linked//6.33825 - 2017-06-21T01:52:14Z - 2017-06-21T01:54:56Z - - John Gruber - http://daringfireball.net/ - - Chris Lattner:

- -
-

Turns out that Tesla isn’t a good fit for me after all. I’m interested to hear about interesting roles for a seasoned engineering leader!

-
- -

That was quick — he was only hired 5 months ago.

- - - - ]]>
-
- - - - - tag:daringfireball.net,2017:/feeds/sponsors//11.33826 - Daring Fireball Department of Commerce - 2017-06-20T23:12:26-04:00 - 2017-06-24T20:23:18-04:00 - Created for discerning Medium.com writers and publishers, it’s the first dedicated desktop publishing client on the Mac App Store. Featured Worldwide on release, it’s the last writing, blogging, and note-taking app you’ll need.

- -

Functional and fun yet mnml af. 🤔 😆 🔥

- - - - ]]>
- [Sponsor] MNML: A macOS Writing App for Medium.com
- The Outline: ‘Inside Apple’s Global War on Leakers’ - - - - tag:daringfireball.net,2017:/linked//6.33824 - 2017-06-20T18:22:34Z - 2017-06-20T18:22:37Z - - John Gruber - http://daringfireball.net/ - - William Turton has quite a scoop for The Outline:

- -
-

A recording of an internal briefing at Apple earlier this month -obtained by The Outline sheds new light on how far the most -valuable company in the world will go to prevent leaks about new -products. […]

- -

The briefing, which offers a revealing window into the company’s -obsession with secrecy, was the first of many Apple is planning to -host for employees. In it, Rice and Freedman speak candidly about -Apple’s efforts to prevent leaks, discuss how previous leakers got -caught, and take questions from the approximately 100 attendees.

-
- -

There’s some irony in a leaked recording of an internal briefing on stopping leaks.

- -

This is news to me:

- -
-

However, Rice says, Apple has cracked down on leaks from its -factories so successfully that more breaches are now happening on -Apple’s campuses in California than its factories abroad. “Last -year was the first year that Apple [campuses] leaked more than the -supply chain,” Rice tells the room. “More stuff came out of Apple -[campuses] last year than all of our supply chain combined.” […]

- -

In the years since Tim Cook pledged to double down on secrecy, -Rice’s team has gotten better at safeguarding enclosures. “In 2014 -we had 387 enclosures stolen,” he says. “In 2015 we had 57 -enclosures stolen, 50 of which were stolen on the night of -announce, which was so painful.” In 2016, Rice says the company -produced 65 million housings, and only four were stolen. “So it’s -about a one in 16 million loss ratio, which is unheard of in the -industry.”

-
- -

There’s a short (15 minute) podcast that accompanies the report, with Turton and The Outline’s Adrianne Jeffries. It’s worth a listen. (It doesn’t seem possible to link directly to a single episode of their podcast, so here’s a direct link for Overcast users.)

- - - - ]]>
-
- The Talk Show: ‘Egg Freckles’ - - - - tag:daringfireball.net,2017:/linked//6.33823 - 2017-06-20T02:02:46Z - 2017-06-20T02:02:48Z - - John Gruber - http://daringfireball.net/ - - New episode of my podcast, The Talk Show, with special guest Serenity Caldwell. We look back at WWDC 2017 — iOS 11, the new iPad Pro models, MacOS 10.13 “High Sierra”, updated Mac hardware and a tease at the upcoming iMac Pro, where Apple might go with VR and AR, San Jose as the venue for the event itself, and more.

- -

Sponsored by:

- -
    -
  • Warby Parker: Boutique-quality, vintage-inspired glasses at a revolutionary price. Try up to five pairs at home for free.
  • -
  • Squarespace: Make your next move. Make your next website. Use code gruber for 10% off your first order.
  • -
  • Fracture: Your photos, printed beautifully directly on glass.
  • -
- - - - ]]>
-
- - - - tag:daringfireball.net,2017://1.33822 - 2017-06-20T00:22:59Z - 2017-06-20T20:08:46Z - - John Gruber - http://daringfireball.net/ - - When people click a URL and see that it’s a Medium site, their reaction should be “Oh, good, a Medium site — this will be nice to read.” Right now it’s gotten to the point where when people realize an article is on Medium, they think, “Oh, crap, it’s on Medium.” - Medium seems to continue to grow in popularity as a publishing platform, and as it does, I’m growing more and more frustrated by their on-screen “engagement” turds. Every Medium site displays an on-screen “sharing” bar that covers the actual content I want to read. This is particularly annoying on the phone, where screen real estate is most precious. Now on iOS they’ve added an “Open in App” button that literally makes the last 1-2 lines of content on screen unreadable. To me these things are as distracting as having someone wave their hand in front of my face while I try to read.

- -

Here’s an annotated screenshot (and threaded rant) I posted to Twitter while trying to read Steven Sinofksy’s WWDC 2017 trip report on my iPad Pro review unit last week.

- -

Safari already has a built-in Sharing button. It has all the options for sharing I need. And as I scroll the page, it disappears so that I can see as much text on screen as possible. Safari is designed to be reader-friendly, as it should be. But it’s trivial to get that Sharing button back when I want it – just tap the bottom of the screen and there it is. Easy.

- -

This is now a very common design pattern for mobile web layouts. Medium is far from alone. It’s getting hard to find a news site that doesn’t put a persistent sharing dickbar down there.

- -

More examples:

- - - -

TechCrunch’s waste of space deserves special mention, for having a persistent navbar at the top and a persistent ad, in addition to their sharing dickbar.

- -

I’m sure “engagement” does register higher with these sharing dickbars, but I suspect a big part of that is because of accidental taps. And even so, what is more important, readability or “engagement”? Medium wants to be about readability but that’s hard to square with this dickbar, and especially hard to square with the “Open in App” button floating above it.

- -

iOS also has a standard way to prompt users to install the app version of a website — Smart App Banners. And it’s user-dismissible.

- -

For any piece over a page long, I read Medium pieces with Safari’s Reader Mode. Medium is supposed to be a reader-optimized layout by default. It should be one of the sites where you’re never even tempted to switch to Reader Mode.

- -

I’m frustrated by this design pattern everywhere I see it. But I’m especially disappointed by Medium’s adoption of it. I don’t expect better from most websites. I do expect better from Medium.

- -

A website should not fight the browser. Let the browser provide the chrome, and simply provide the content. Web developers know this is right — these dickbars are being rammed down their throats by SEO experts. The SEO folks are the same dopes who came up with the genius strategy of requiring 5-10 megabytes of privacy-intrusive CPU-intensive JavaScript on every page load that slows down websites. Now they come to their teams and say, “Our pages are too slow — we gotta move to AMP so our pages load fast.”

- -

I don’t expect to break through to the SEO shitheads running the asylums at most of these publications, but Medium is supposed to be good. When people click a URL and see that it’s a Medium site, their reaction should be “Oh, good, a Medium site — this will be nice to read.” Right now it’s gotten to the point where when people realize an article is on Medium, they think, “Oh, crap, it’s on Medium.”

- - - - ]]>
- ★ Medium and the Scourge of Persistent Sharing Dickbars
- Microsoft Surface Laptop Teardown - - - - tag:daringfireball.net,2017:/linked//6.33821 - 2017-06-19T19:43:57Z - 2017-06-19T20:07:54Z - - John Gruber - http://daringfireball.net/ - - iFixit gave the Surface Laptop a 0 out of 10 on their “Repairability Score”. The lowest anything from Apple has ever gotten is a 1, I believe.

- -
-

Verdict: The Surface Laptop is not a laptop. It’s a glue-filled -monstrosity. There is nothing about it that is upgradable or -long-lasting, and it literally can’t be opened without destroying -it. (Show us the procedure, Microsoft, we’d love to be wrong.)

-
- -

iFixit’s point of view on this is logical, and they’re certainly not alone in wishing for the good old days of user-accessible and user-upgradeable components. But it’s silly to argue that the Surface Laptop is “not a laptop” only because it’s a sealed box. It’s like saying the iPhone is not a phone because it doesn’t have a replaceable battery.

- -

Update: Apple’s AirPods got a 0/10 from iFixit. That just goes to show how little correlation there is between iFixit’s concept of repairability and whether a product is good or not. I consider AirPods to be Apple’s best new product in years.

- - - - ]]>
-
- Standard Ebooks - - - - tag:daringfireball.net,2017:/linked//6.33820 - 2017-06-19T19:40:57Z - 2017-06-20T00:37:38Z - - John Gruber - http://daringfireball.net/ - - Standard Ebooks:

- -
-

Standard Ebooks is a volunteer driven, not-for-profit project -that produces lovingly formatted, open source, and free public -domain ebooks.

- -

Ebook projects like Project Gutenberg transcribe ebooks and -make them available for the widest number of reading devices. -Standard Ebooks takes ebooks from sources like Project -Gutenberg, formats and typesets them using a carefully designed -and professional-grade style guide, lightly modernizes them, -fully proofreads and corrects them, and then builds them to -take advantage of state-of-the-art ereader and browser -technology. […]

- -

Other free ebooks don’t put much effort into professional-quality -typography: they use "straight" quotes instead of “curly” quotes, -they ignore details like em- and en-dashes, and they look more -like early-90’s web pages instead of actual books.

- -

The Standard Ebooks project applies a rigorous and modern -typography manual when developing each and every ebook to ensure -they meet a professional-grade and consistent typographical -standard. Our ebooks look good.

-
- -

What a fantastic project. Project Gutenberg is an amazing library, but their books are a mess typographically. (Via Daniel Bogan.)

- - - - ]]>
-
- The Size of iPhone’s Top Apps Has Increased by 1,000 Percent in Four Years - - - - tag:daringfireball.net,2017:/linked//6.33819 - 2017-06-19T19:23:51Z - 2017-06-19T19:23:52Z - - John Gruber - http://daringfireball.net/ - - Randy Nelson, writing for the Sensor Tower blog:

- -
-

According to Sensor Tower’s analysis of App Intelligence, -the total space required by the top 10 most installed U.S. iPhone -apps has grown from 164 MB in May 2013 to about 1.8 GB last month, -an 11× or approximately 1,000 percent increase in just four years. -In the following report, we delve deeper into which apps have -grown the most.

-
- -

Apple really needs to do something about this. It’s not just that these apps are too big, but some of them issue software updates every week (or even more frequently). It’s a huge waste of bandwidth, time, and on-device storage space.

- - - - ]]>
-
- Microsoft AI Team Achieves Perfect Score on Atari 2600 Ms. Pac-Man - - - - tag:daringfireball.net,2017:/linked//6.33818 - 2017-06-19T19:14:49Z - 2017-06-19T19:17:41Z - - John Gruber - http://daringfireball.net/ - - Dani Deahl writing for The Verge:

- -
-

At long last, the perfect score for arcade classic Ms. Pac-Man has -been achieved, though not by a human. Maluuba — a deep learning -team acquired by Microsoft in January — has created an AI system -that’s learned how to reach the game’s maximum point value of -999,900 on Atari 2600, using a unique combination of reinforcement -learning with a divide-and-conquer method.

-
- -

Unlike the notoriously bad 2600 port of Pac-Man, the Ms. Pac-Man port was both fun and true to the spirit of the coin-op.

- - - - ]]>
-
- Why Reach Navigation Should Replace the Navbar in iOS Design - - - - tag:daringfireball.net,2017:/linked//6.33817 - 2017-06-19T18:52:46Z - 2017-06-19T18:57:25Z - - John Gruber - http://daringfireball.net/ - - Brad Ellis:

- -
-

As devices change, our visual language changes with them. It’s -time to move away from the navbar in favor of navigation within -thumb-reach. For the purposes of this article, we’ll call that -Reach Navigation.

-
- -

This design trend is clearly already underway, and Ellis does a terrific job explaining why it’s a good idea.

- -

I can think of a few factors that led to the original iPhone having a top-of-the-screen UI for navigation. First, at just 3.5 inches diagonally, the whole screen was reachable. But another factor might be as simple as the fact that “navigation” was always at the top on desktops — window titles and controls have always been at the top on Mac and Windows. The iPhone didn’t use windows, per se, but there was a certain familiarity with having the titles and controls like Back/Close/Done buttons at the top. Something like the iOS 10 bottom-heavy design of Apple Maps is wholly different from a desktop UI design — as it should be.

- - - - ]]>
-
- Ben Thompson on Amazon and Whole Foods - - - - tag:daringfireball.net,2017:/linked//6.33816 - 2017-06-19T18:25:15Z - 2017-06-19T18:25:16Z - - John Gruber - http://daringfireball.net/ - - Great piece by Ben Thompson on Amazon’s intended acquisition of Whole Foods:

- -
-

As Mackey surely understood, this meant that AmazonFresh was at a -cost disadvantage to physical grocers as well: in order to be -competitive AmazonFresh needed to stock a lot of perishable items; -however, as long as AmazonFresh was not operating at meaningful -scale a huge number of those perishable items would spoil. And, -given the inherent local nature of groceries, scale needed to be -achieved not on a national basis but a city one.

- -

Groceries are a fundamentally different problem that need a -fundamentally different solution; what is so brilliant about this -deal, though, is that it solves the problem in a fundamentally -Amazonian way.

-
- - - - ]]>
-
- Ikea Details Plans for Furniture Placement App Powered by Apple’s ARKit - - - - tag:daringfireball.net,2017:/linked//6.33815 - 2017-06-19T17:59:44Z - 2017-06-19T17:59:46Z - - John Gruber - http://daringfireball.net/ - - Mitchel Broussard:

- -
-

At WWDC this year, Apple senior vice president of software -engineering Craig Federighi performed a demo of the company’s new -augmented reality platform, ARKit, while mentioning popular -furniture company IKEA as an upcoming partner in the technology. -Similarly, Apple CEO Tim Cook referenced an Ikea AR partnership in -a recent interview with Bloomberg Businessweek.

- -

Now, Ikea executive Michael Valdsgaard has spoken about the -company’s partnership with Apple and ARKit, describing an all-new -augmented reality app that will help customers make “reliable -buying decisions” for Ikea’s big ticket items.

-
- -

Very cool idea — probably the sort of thing that’s going to be common soon. I’m curious how much of a leg up ARKit will give iOS on this front.

- - - - ]]>
-
- Squarespace Domains - - - - tag:daringfireball.net,2017:/linked//6.33814 - 2017-06-18T18:57:06Z - 2017-06-19T03:26:42Z - - John Gruber - http://daringfireball.net/ - - My thanks to Squarespace for sponsoring last week’s DF RSS feed. Squarespace handles everything related to creating, hosting, and maintaining a website, including domain name registration.

- -

Buying a domain from Squarespace is quick, simple, and fun. Search for the domain you want, or type any word or phrase into the search field and Squarespace will suggest some great options. Every domain comes with a beautiful, ad-free parking page, WHOIS Privacy, and a 2048-bit SSL certificate to secure your website — all at no additional cost. Once you lock down your domain, create a beautiful website with one of Squarespace’s award-winning templates. Try Squarespace for free. When you’re ready to subscribe, get 10% off at squarespace.com with offer code “DARING17”.

- - - - ]]>
-
- Designing the Worst Volume Sliders Possible - - - - tag:daringfireball.net,2017:/linked//6.33813 - 2017-06-18T18:54:31Z - 2017-06-18T18:54:32Z - - John Gruber - http://daringfireball.net/ - - This is a fun challenge.

- - - - ]]>
-
- John Markoff to Interview Scott Forstall Next Week - - - - tag:daringfireball.net,2017:/linked//6.33812 - 2017-06-15T19:34:33Z - 2017-06-15T20:03:29Z - - John Gruber - http://daringfireball.net/ - - Blockbuster event next week at the Computer History Museum in Mountain View:

- -
-

How did iPhone come to be? On June 20, four members of the -original development team will discuss the secret Apple project, -which in the past decade has remade the computer industry, changed -the business landscape, and become a tool in the hands of more -than a billion people around the world.

- -

Part 1: Original iPhone Engineers Nitin Ganatra, Scott Herz, and -Hugo Fiennes in Conversation with John Markoff

- -

Part 2: Original iPhone Software Team Leader Scott Forstall in -Conversation with John Markoff

-
- -

It kills me that I can’t make this. Hopefully there will be video.

- -

Here’s the thing: Forstall was obviously a divisive figure inside Apple. He saw himself as an indispensable man after Steve Jobs died, and it turns out he wasn’t.

- -

But there can be no dispute that Forstall led one of the most successful software projects ever undertaken. It’s a cliche to say that they achieved the impossible, but what Forstall’s team achieved was considered by many — including many of the members of the team — impossible. But they did it, and in the ensuing years they kept making iOS better and better. It’s not just that they managed to ship the original iPhone OS in June 2007, but the entire run up through Forstall’s ouster from the company was simply amazing.

- -

Across the company, it’s clear that Forstall’s style was not popular. But I know many people who worked on his iOS team, and most of them loved working for him, or at the very least appreciated working for him. The thing I’ve heard over and over is that Forstall was incredibly demanding, yes, but if you were on his team and did good work he had your back.

- -

Forstall pretty much hasn’t said a damn thing about Apple since he left the company five years ago. So if he opens up at all to Markoff, this could be fascinating. His team’s story about actually implementing the original iPhone remains largely untold.

- - - - ]]>
-
- Brian Merchant Has Tony Fadell on Tape - - - - tag:daringfireball.net,2017:/linked//6.33811 - 2017-06-15T19:16:54Z - 2017-06-15T19:16:55Z - - John Gruber - http://daringfireball.net/ - - Nilay Patel, announcing a special episode of The Vergecast with The One Device author Brian Merchant:

- -
-

And, of course, we talk about the quotes from Tony Fadell and -Brett Bilbrey in the excerpt we just published, in which Fadell -tells a story about Phil Schiller arguing the iPhone should have a -hardware keyboard. Schiller has said the story isn’t true, and -Fadell has tried to walk it back as well.

- -

“So I wasn’t in the room at Apple 10, 15 years ago when this would -have happened,” says Merchant, who has the exchange on tape. “But -this is a quote verbatim as Tony Fadell who was in the room told -it to me. He told me this quote in such detail and he gave such a -vivid account, and I had no reason to believe it was untrue.”

- -

Merchant says the controversy has “blown him away.”

-
- -

I figured Merchant had Fadell’s interview recorded. The quotes were too extensive not to have been recorded. It’s pretty clear what happened: Fadell told Merchant exactly what he’s quoted as saying, but now that he’s seen how it’s playing out, he wants to walk it back. It’s a little late for that.

- - - - ]]>
-
- - - - tag:daringfireball.net,2017://1.33805 - 2017-06-14T03:59:00Z - 2017-06-14T21:22:44Z - - John Gruber - http://daringfireball.net/ - - Highlights from the best of yesterday’s iPad Pro reviews. - Federico Viticci, MacStories, “The 10.5-inch iPad Pro: Future-Proof”:

- -
-

A good way to think about the iPad’s new display with ProMotion -is not the difference between low-res and Retina screens, but -the jump from 30fps to 60fps. You see more of every animation. -Text is more legible when you scroll and doesn’t judder. It’s -hard to explain and it has to be seen and experienced to be -fully understood. Every scroll, page transition, and app launch -animation on the 10.5″ iPad Pro is absurdly smooth to the point -of feeling unrealistic at first — hence the common reaction -that something doesn’t quite compute. But as you spend some time -with the new iPad and start using it on a daily basis, its -display becomes normal and you wish that other Apple displays -were the same.

- -

I’m not even a week into my tests with the 10.5″ iPad Pro, and -I think scrolling on my first-gen 12.9″ iPad Pro looks choppy -now. I’d be surprised if 120Hz displays with ProMotion don’t -expand to the iPhone later this year and other Apple computers -in the future. The combination of hardware and software really -is that good.

-
- -

Last year when True Tone was introduced with the 9.7-inch iPad Pro, Phil Schiller said something to the effect of “Once you get used to True Tone, you can’t go back.” I optimistically took that as a sign that the iPhone 7 would have True Tone. It did not, and the reason is probably that True Tone requires additional hardware sensors on the front face to pick up the ambient light temperature, and the iPhone has less room for additional sensors. But with ProMotion, I’m really hopeful that it’ll make its way into this year’s new iPhones. ProMotion doesn’t require additional sensors — only a super-fast GPU (which the iPhone will have) and intricate software support in iOS (which work Apple has already done for the iPad Pro).

- -

Anyway, it’s really hard to quote just one bit from Viticci’s review. If you only thoroughly read one review of the new iPad Pro, it should be his. Nobody outside Apple cares as much about iPad as he does.

- -
- -

Matthew Panzarino, TechCrunch, “Apple Pays Off Its Future-of-Computing Promise With iPad Pro”:

- -
-

After playing with the new iPad Pro 10.5” for a few days, I am -convinced that it’s fairly impossible to do a detailed review of -it in its current state.

- -

Not because there is some sort of flaw, but because it was clearly -designed top to bottom as an empty vessel in which to pour iOS 11.

- -

Every feature, every hardware advancement, every piece of -understated technical acrobatics is in the service of making -Apple’s next-generation software shine.

-
- -
- -

Dieter Bohn, The Verge, “iPad Pro 10.5 Review: Overkill”:

- -
-

I was all set to complain that increasing the size from 9.7 to -10.5 was not a big enough jump to justify requiring people to buy -new keyboards and accessories. Then I started typing on the -on-screen keyboard and on the new hardware Smart Keyboard. Even -though I’m dubious about Apple’s claim that the software keyboard -is “full size”, I find the slight size increase makes touch typing -much easier. It’s still a little cramped, but it’s much easier to -bounce between this and a real keyboard now.

-
- -

It really does make a difference in typing, and no practical difference at all in terms of holdability.

- -

Bohn again:

- -
-

To me, if you’re going to spend $650 on a computer, it should -almost surely be your main computer. And if you’re going to make -the iPad Pro your main computer, you should probably get more than -64GB of storage and you should also probably get a keyboard to go -with it (to say nothing of the Apple Pencil). It hits the $1,000 -mark very quickly.

-
- -

I don’t agree with the notion that a $650 computer should be your “main computer” at all. Apple stuff isn’t for the budget-conscious — news at 11.

- -
- -

Brian X. Chen, The New York Times, “New iPad Pro Inches Toward Replacing PC, but Falls Short”:

- -
-

Five years later, Mr. Jobs’s successor, Timothy D. Cook, took the -iPad a step further. Unveiling the iPad Pro, a souped-up tablet -that worked with Apple’s keyboard and stylus, he remarked that -people would try the product and “conclude they no longer need to -use anything else, other than their phones.”

- -

That prediction has not appeared to come true. Many professionals -say they use an iPad in addition to a personal computer, and sales -of iPads have shrunk quarter after quarter for more than a year, -an indication that hordes of people were not trading in their PCs -for tablets just yet.

- -

That situation is unlikely to change with Apple’s newest iPad Pro, -which will be released this week. […] But after about a week of -testing the 10.5-inch iPad Pro, I concluded that Apple’s -professional tablet still suffers from some of the same problems -when compared with a laptop.

-
- -

That’s a slanted truncation of Cook’s quote. Cook’s full quote: “Yes, the iPad Pro is a replacement for a notebook or a desktop for many, many people. They will start using it and conclude they no longer need to use anything else, other than their phones.” Chen’s truncation makes it sound like Cook claimed the iPad Pro was a Mac or Windows replacement for everyone. He didn’t. And the fact that the new iPad Pro debuted alongside new MacBooks, MacBook Pros, and even more-megahertz-in-the-box MacBook Airs shows that Apple doesn’t think so either. Update: And I completely forgot to mention the solid updates to the iMacs and the announcement of the iMac Pro.

- -

I prefer a laptop to an iPad Pro” is very different from “A laptop is better than an iPad Pro”. Me, personally, I much prefer working on a MacBook Pro to an iPad Pro. But I can see why others feel the opposite. That’s the whole point of Apple’s strategy of keeping them separate, rather than unifying them Microsoft Surface-style.

- -

iPad’s slowly diminishing sales are a real thing. But I don’t think that can be used as a gauge for whether more and more people are using an iPad as their main computer. And iPad sales are still more than double those of the Mac. There’s no reason to doubt that “many, many people” are concluding they no longer need a Mac or PC.

- -
- -

Andrew Cunningham, Ars Technica, “The 10.5-Inch iPad Pro Is Much More “Pro” Than What It Replaces”:

- -
-

Of all the computers Apple sells, none of them has screens that do -quite as much stuff as the iPad Pros are doing.

- -

That list starts with DCI-P3 color gamut support (new in the -12.9-inch Pro, returning to the smaller one) and an -anti-reflective coating, features also present in recent iMacs and -MacBook Pros. But the True Tone feature, which detects the color -temperature of the ambient light, adjusts the display’s color -temperature to match. Most significantly, the iPad’s refresh rate -has been bumped up to 120Hz, twice the normal 60Hz. The screens in -the iPad Pros are the best screens Apple ships, which is -appropriate for a thing that’s just a giant screen by design.

- -

The 10.5-inch Pro has a 2224×1668 screen, up just a little bit -from the 2048×1536 in 9.7-inch iPads. The density is identical, so -photos and text are exactly the same size they were before; you -can just fit a bit more of them on-screen at once.

-
- -

That’s important to note. There was some clever speculation by Dan Provost a few months ago that the 10.5-inch iPad would have the same pixel dimensions as the 12.9-inch iPad Pro, with a higher pixels-per-inch density. That’s what Apple did with the iPad Mini. The problem with that speculation is that while the math worked out, the size of things on screen would not. Everything would be shrunk by 20 percent. Not everyone’s eyes can handle that. That’s fine for the Mini — which is often used by sharp-eyed children — but not fine for the standard size iPad.

- -

I had been thinking that maybe what Apple would do is what Provost suggested, but offer a choice between standard and zoomed mode like the Plus-sized iPhones do. Nope. I think what they’ve done is better though, because I think a scaled “zoomed” interface would look blurry at just 324 ppi. The iPhone Plus displays have a resolution of 401 ppi.

- -
- -

Harry McCracken, Fast Company, “A Better Window Into The World Of Apps”:

- -
-

You can suss out a lot about Apple’s priorities from the aspects -of a product it leaves alone and the ones it never stops -obsessing over.

- -

Consider the iPad. Every generation of Apple’s tablet since the -first one in 2010 has had the same stated battery life–“up to 10 -hours”–which suggests that the company thinks that shooting for -anything in excess of that would be wasted effort.

- -

That 2010 iPad weighed a pound and a half, and felt a bit hefty in -the hand. With 2013’s iPad Air, Apple whittled that down to about -a pound. And there the mid-sized iPads have stayed, -weight-reduction mission accomplished.

- -

However, when it comes to the iPad’s display, Apple has never been -satisfied to rest on its technological laurels.

-
- - - - ]]>
- ★ iPad Pro Review Roundup
- - - - tag:daringfireball.net,2017://1.33799 - 2017-06-13T20:46:25Z - 2017-06-13T23:15:31Z - - John Gruber - http://daringfireball.net/ - - I do know that Schiller’s hard-charging, brusque style and his obvious political acumen have made him a lot of enemies over the years. It sounds like Fadell is one of them. - The Verge has an exclusive (and lengthy) excerpt from Brian Merchant’s The One Device: The Secret History of the iPhone, which comes out next week. Merchant seemingly has many first-hand sources on the record, including Tony Fadell and perhaps Scott Forstall. (I say “perhaps” because it’s not clear from the excerpt whether Forstall spoke to Merchant, or if Merchant got the Forstall quotes from somewhere else. It seems like there should be a lot more from Forstall in this story if he actually talked to Merchant.)

- -

But Fadell spoke to Merchant extensively, including this shot at Phil Schiller:

- -
-

The iPod phone was losing support. The executives debated which -project to pursue, but Phil Schiller, Apple’s head of marketing, -had an answer: Neither. He wanted a keyboard with hard buttons. -The BlackBerry was arguably the first hit smartphone. It had an -email client and a tiny hard keyboard. After everyone else, -including Fadell, started to agree that multitouch was the way -forward, Schiller became the lone holdout.

- -

He “just sat there with his sword out every time, going, ‘No, -we’ve got to have a hard keyboard. No. Hard keyboard.’ And he -wouldn’t listen to reason as all of us were like, ‘No, this works -now, Phil.’ And he’d say, ‘You gotta have a hard keyboard!’” -Fadell says.

-
- -

I don’t know if it’s true or not that Schiller was singlehandedly pushing for a Blackberry-style keyboard. But even if true, it only looks foolish in hindsight, especially if this argument took place before the iPhone’s software team had come up with a proof-of-concept software keyboard. Today it’s clear that the iPhone needed a good keyboard, and that a touchscreen keyboard can be a good keyboard. Neither of those things was obvious in 2005. And in the context of this story, it’s clear that at the time of this purported argument, Steve Jobs and Apple weren’t yet sure if the iPhone should be a pocket-sized personal computer or a consumer electronics product that would have no more need for a keyboard (hardware or software) than an iPod did. My guess is that Schiller was insisting that the iPhone needed to be a personal computer, not a mere gadget, and it wasn’t unreasonable to believe a software keyboard wouldn’t be good enough. For chrissakes there were critics who insisted that the iPhone’s software keyboard wasn’t good enough for years after the iPhone actually shipped.

- -

I do know that Schiller’s hard-charging, brusque style and his obvious political acumen have made him a lot of enemies over the years. It sounds like Fadell is one of them.

- -

So I’ll just say this: this story about Phil Schiller pushing for a hardware keyboard comes from one source (so far — if anyone out there can back that up, my window is always open for little birdies), and that one source is the guy who admittedly spent over a year working on iPhone prototypes with a click wheel interface.

- -

Then there’s this:

- -
-

Schiller didn’t have the same technological acumen as many of the -other execs. “Phil is not a technology guy,” Brett Bilbrey, the -former head of Apple’s Advanced Technology Group, says. “There -were days when you had to explain things to him like a -grade-school kid.” Jobs liked him, Bilbrey thinks, because he -“looked at technology like middle America does, like Grandma and -Grandpa did.”

-
- -

Hats off to Bilbrey for putting his name on this quote, but having spoken to Schiller both on- and off-the-record many times, the idea that he “looks at technology … like Grandma and Grandpa did” and needs things explained to him “like a grade-school kid” is bullshit. Especially off-the-record, Schiller can drill down on technical details to a surprising degree. I don’t know what Schiller did to piss off Bilbrey, but Bilbrey either has a huge chip on his shoulder or was severely misquoted by Merchant.1

- -

Anyway, I sure wish this book excerpt had come out before my live episode of The Talk Show last week — now I do have one more question I wish I’d gotten to ask Schiller.

- -
-
-
    -
  1. -

    Here’s a story from Yoni Heisler for Network World on Brett Bilbrey’s retirement from Apple in 2014. Bilbrey headed Apple’s Technology Advancement Group. Merchant describes Bilbrey as having led “Apple’s Advanced Technology Group”. It’s a small detail, and the names are clearly similar, but the Advanced Technology Group was Larry Tesler’s R&D division at Apple, from 1986-1997. It was among the numerous divisions and products that Steve Jobs shitcanned after he rejoined the company. ↩︎

    -
  2. -
-
- - - - ]]>
- ★ The Knives Come Out for Phil Schiller in Brian Merchant’s ‘The One Device: The Secret History of the iPhone’
- - - - tag:daringfireball.net,2017://1.33796 - 2017-06-12T22:33:42Z - 2017-06-13T23:15:34Z - - John Gruber - http://daringfireball.net/ - - The new iPad Pro hardware is almost too good to be true, but the real iPad story Apple unveiled last week is iOS 11. - I’ve spent the last week using a new 10.5-inch iPad Pro, and this is, in many ways, the easiest product review I’ve ever written. There are several significant improvements to the hardware, and no tradeoffs or downsides. There is no “but”.

- -
    -
  • Display: The new iPad Pros have the best displays of any computer I’ve ever seen. True Tone plus ProMotion is simply terrific. (The first generation 12.9-inch iPad Pro didn’t have True Tone; with these new models, the only noticeable difference between the 12.9- and 10.5-inch models is the size.) You really do have to see the 120 Hz refresh rate in person — and play with it while scrolling content on screen — to get it. You can actually read text as it’s moving during a scroll. It’s not as significant as the jump from non-retina to retina, but it’s in that ballpark.

  • -
  • Pencil: The latency of the Apple Pencil on a first-generation iPad Pro is the best I had ever seen for any stylus on any device at any price. The latency of the Apple Pencil on the new iPad Pro is so much better — so much closer to ink-on-paper imperceptibility — that you have to try it to believe it. It’s the one thing that really makes the first-gen iPad Pro feel “slow”.

  • -
  • Size: The increase in size is perfect. The footprint for a “regular” iPad has, until now, remained unchanged since the original iPad in 2010. That 9.7-inch display size was nearly perfect. This 10.5-inch display size is better though. Apple said during the keynote that typing on the on-screen keyboard is surprisingly better given just a bit more room, and I agree. And typing on the Smart Keyboard cover is way better than on last year’s 9.7-inch iPad Pro. In hand it doesn’t feel bigger at all. It feels like there were no trade-offs whatsoever in increasing the display size and overall device footprint. Part of that is because the weight has remained completely unchanged. I have had zero problems — not one — with the decreased bezel area. Apple’s inadvertent touch detection game is on point.

  • -
  • Battery: Battery life is great, as expected.

  • -
  • Performance: Apple’s in-house chip team continues to amaze. No one buys an iPad because of CPU benchmarks, but the new iPad Pro’s CPU performance is mind-boggling. Forget about comparisons to the one-port MacBook — the iPad Pro blows that machine out of the water performance-wise. The astounding thing is that the new iPad Pro holds its own against the MacBook Pro in single-core performance — around 3,900 on the Geekbench 4 benchmark for the iPad Pro vs. around 4,200–4,400 for the various configurations of 13- and 15-inch MacBook Pros.1 Multi-core performance has effectively doubled from the first generation of iPad Pro. That sort of year-over-year increase just doesn’t happen anymore, but here we are. The new iPad Pro gets a multi-core Geekbench 4 score of around 9200; the brand-new Core M3-based MacBook gets a multi-core score of around 6800. Again, this isn’t why people buy iPads — the iPad took off like a rocket in 2010 back when it was way slower (way way way slower) than even the slowest MacBook — but I think it is vastly underappreciated just how significantly Apple’s chip team is pulling ahead of the industry, especially Intel.2

  • -
- -
- -

All that said, the real story of these new iPad Pro models can’t be told today, because that story is iOS 11. I think iOS 11’s iPad-focused features are the entire reason why Apple waited until WWDC to unveil them. They could have held an event for them back in April, when they released the new starting-at-just-$329 9.7-inch iPad, but if they did, the only new software they could have demoed was Clips. I love Clips, but it’s just a fun little tool and doesn’t show off anything particular to iPad compared to iPhone.

- -

Again, the new iPad Pro hardware is almost too good to be true, but the iPad story Apple unveiled last week is iOS 11.

- -

It’s not fair to review a product running a developer beta of the OS — let alone the first (and generally buggiest) beta. So let’s stop the “review” right here: the new iPad Pros running iOS 10.3.2 are the best iPads ever made. You shouldn’t hesitate to buy one today, and if you do get one now, you should wait until iOS 11 ships in the fall to upgrade, or at the very least wait for a non-developer public beta of iOS 11 this summer before upgrading.

- -

But if you are reckless enough to install the iOS 11 beta on the new iPad Pro? Holy smokes is this better. I used the iPad Pro for a full week with iOS 10.3.2 because that’s the product that’s shipping, but after upgrading to iOS 11 beta 1 this morning and using it to write this entire review,3 I’m just blown away by how much more useful this machine is, and how much easier it is to work with 5 or 6 apps at a time.

- -

I would never recommend running a beta of any OS on any device that’s used for production purposes, so don’t take this as such, but for me personally, I can’t see going back to iOS 10.3.2 on any iPad that can handle it. It feels like a hand has been untied from behind my back, and this amazing hardware has finally been allowed to run free.

- -
-
-
    - -
  1. -

    You can browse Geekbench’s database of results for Mac and iOS↩︎

    -
  2. - -
  3. -

    Apple’s A10X chip is so high-performing that I think it’s put Apple in a slightly uncomfortable position marketing-wise. They can’t brag about it fully without making Intel (and by implication, their own MacBooks) look bad, and Intel remains an important partner for Apple. I don’t think it’s a coincidence that the most impressive iPad demo during the keynote (and the one that contained the most bragging about its performance compared to PCs) was done by a third-party developer — Ash Hewson of Serif, demonstrating Affinity Photo — not Greg Joswiak or anyone else from Apple. ↩︎︎

    -
  4. - -
  5. -

    Full disclosure: I went back to my Mac to write these 3 footnotes. That’s due more to the byzantine way I mark up footnotes than any limitation inherent to iOS vs. MacOS. But it feels worth noting. ↩︎︎

    -
  6. - -
-
- - - - ]]>
- ★ The 2017 iPad Pros
- - - - tag:daringfireball.net,2017://1.33791 - 2017-06-10T20:20:10Z - 2017-06-10T20:20:12Z - - John Gruber - http://daringfireball.net/ - - Given how many legitimate developers are still having problems getting their apps approved due to seemingly capricious App Store reviewer decisions, it’s doubly outrageous that these apps have made their way onto the store in the first place. These are the *exact* sort of apps that the App Store review process should be primarily looking to block. - Great investigative piece by Johnny Lin looking into a top-10 highest grossing app named “Mobile protection :Clean & Security VPN” (punctuation and grammatical errors sic), from a developer named Ngan Vo Thi Thuy:

- -
-

“Full Virus, Malware scanner”: What? I’m pretty sure it’s -impossible for any app to scan my iPhone for viruses or malware, -since third party apps are sandboxed to their own data, but let’s -keep reading…

- -

“You will pay $99.99 for a 7-day subscription”

- -

Uhh… come again?

-
- -

There should be no “virus and malware” scanners in the App Store. None. iOS does not need anti-virus software. The App Store sandboxing rules mean that anti-virus software couldn’t really do anything useful anyway. And by allowing them to be listed on the store, it creates the false impression that Apple thinks you might need anti-virus software.

- -

But do-nothing anti-virus utilities that are scamming people into $100/week subscriptions? That’s downright criminal.

- -

Lin shows that “Mobile protection :Clean & Security VPN” is not alone. The productivity top-grossing list is riddled with similar scam apps.

- -

Given how many legitimate developers are still having problems getting their apps approved due to seemingly capricious App Store reviewer decisions, it’s doubly outrageous that these apps have made their way onto the store in the first place. These are the exact sort of apps that the App Store review process should be primarily looking to block.

- -

And there is no excuse for Apple not having flagged them after the fact, once they started generating significant revenue. It’s downright mind boggling that this horrendous “Mobile protection :Clean & Security VPN” app made it all the way into the top 10 without getting flagged.

- -

Based on Lin’s research, the pattern is simple:

- -
    -
  1. Create a scammy utility app and get it into the store. Make it a free download with an expensive in-app subscription.
  2. -
  3. Bid on common keywords like “virus”, “VPN”, and “wi-fi”.
  4. -
  5. Get tens of thousands of downloads thanks to the top-of-results placement from the ad.
  6. -
  7. Spring the in-app subscription prompt on your users and make money with a response rate of even just a fraction of 1 percent.
  8. -
- -

Apple needs to remove these apps from the App Store, and prevent such apps from getting into the store in the first place. They should reconsider the effects of allowing developers to buy their way to the top spot in search results. And they should police the top-grossing lists for apps that are pulling scams — the most important scams to catch are the successful ones.

- -

Lastly, every single dollar these apps have generated should be refunded to the victims of these scams.

- - - - ]]>
- ★ In-App Purchase Scams in the App Store
- - - - tag:daringfireball.net,2017://1.33772 - 2017-06-02T05:41:06Z - 2017-06-02T06:13:18Z - - John Gruber - http://daringfireball.net/ - - The next batch of tickets will go on sale today, Friday, at 1p ET/10a PT. Given what happened Wednesday, I expect them to sell out in a few minutes. - On Wednesday I put the first 500 tickets on sale for next week’s live show from WWDC. They sold out in 7 minutes.

- -

The California Theatre in San Jose has both an orchestra level and a balcony. That first bunch of tickets separated the two. After talking with the staff at the theater today, they recommended making all tickets general admission and allowing their ushers to fill the orchestra level first, and then direct remaining ticket holders to the balcony. So, all tickets, including those sold Wednesday, are now simply general admission. Everyone paid the same price, so I think this is fair, but I do apologize for any confusion. The theater is beautiful, and there are no bad seats.

- -

The next batch of tickets will go on sale today, Friday, at 1p ET/10a PT. Given what happened Wednesday, I expect them to sell out in a few minutes. I hate writing that because it sounds braggy, but I’m putting it out there just as fair warning. You’re going to have to act quick and maybe get lucky.

- -

If you want a ticket and wind up not getting one, there will be a live audio stream for everyone to listen to. This year we are not going to attempt to stream live video. Instead we’re going to work hard to get edited video of the event up on the web as soon as possible after the show is over. If you just can’t wait, listen to the live audio. If you want to see the show, wait for the video — it should be up some time on Wednesday at the latest.

- -

If you do get a ticket or already have one:

- -
    -
  • All seats are general admission, with no distinction between orchestra level and the balcony.
  • -
  • Put your ticket in Apple Wallet and bring ID to show at the door. (If you don’t have an iPhone (?) bring a copy of the ticket PDF.)
  • -
  • Doors open at 6p, and there will be an open bar. Find a seat, grab a beverage, and mingle with your fellow fans of the show.
  • -
  • The show itself should start at 7p.
  • -
- - - - ]]>
- ★ Update on The Talk Show Live From WWDC 2017
diff --git a/Frameworks/RSParser/RSParserTests/Resources/DaringFireball.html b/Frameworks/RSParser/RSParserTests/Resources/DaringFireball.html deleted file mode 100755 index 690218708..000000000 --- a/Frameworks/RSParser/RSParserTests/Resources/DaringFireball.html +++ /dev/null @@ -1,1341 +0,0 @@ - - - - - Daring Fireball - - - - - - - - - - - - -
- - - - - -
- -
-
-San Bernardino DA Says Seized iPhone May Hold ‘Dormant Cyber Pathogen’  -
-
-

Ars Technica:

- -
-

“The iPhone is a county owned telephone that may have connected to the San Bernardino County computer network. The seized iPhone may contain evidence that can only be found on the seized phone that it was used as a weapon to introduce a lying dormant cyber pathogen that endangers San Bernardino’s infrastructure,” according to a court filing (PDF) by Michael Ramos, the San Bernardino County district attorney.

-
- -

Sounds to me like Ramos has watched Skyfall too many times.

- -
- - -
- - -
-
-Igloo, a Modern Intranet  -
-
-

My thanks to Igloo for sponsoring this week’s DF RSS feed. Collaboration can be incredibly fragmented today — files shared one way, messaging via various chat apps, email lists for groups, etc. It can be overwhelming.

- -

That’s why you should try Igloo. It combines department spaces, team calendars, corporate file sharing, internal communications capabilities, social features, and more. It’s really easy both to use and to configure. Igloo is an intranet you’ll actually like. Try Igloo today, free of charge.

- -
- - -
- - -
-
-Apple Hosts Public Letter From San Bernardino Victim to Judge Sheri Pym (PDF)  -
-
-

Salihin Kondoker:

- -
-

When I first learned Apple was opposing the order I was frustrated that it would be yet another roadblock. But as I read more about their case, I have come to understand their fight is for something much bigger than one phone. They are worried that this software the government wants them to use will be used against millions of other innocent people. I share their fear.

- -

I support Apple and the decision they have made. I don’t believe Tim Cook or any Apple employee believes in supporting terrorism any more than I do. I think the vicious attacks I’ve read in the media against one of America’s greatest companies are terrible.

-
- -

The battle is being fought both in the courtroom, and in the court of public opinion. Support like this helps Apple with the latter — which in turn helps with the former.

- -
- - -
-Box Chief Information Security Officer’s Perspective on Apple and the FBI  -
-
-

Joel de la Garza:

- -
-

I’ve been working to help secure computer systems for the entirety of my professional career. It is incredibly difficult to build computer systems that are not vulnerable to attack. As we’ve seen, a number of companies and governments have had great difficulty protecting the front door of their computer systems. I’ve dedicated my career to making sure our systems are designed, built, and operated to the most secure standards. And even with that tremendous investment, bugs still happen. Due to the many layers of security controls built into our systems software bugs are usually not damaging or catastrophic in nature. But peeling away those layers of control to create a backdoor means that even the most basic security bug could potentially have a catastrophic effect. This reality is missing from our current debate about the FBI’s order to Apple in the San Bernardino tragedy.

-
- -
- - -
-@AppleSupport  -
-
-

Neat: Apple has a new Twitter account for tips, tricks, and support. Right now they’re answering 3-4 questions per minute.

- -
- - -
-Full Frontal With Samantha Bee  -
-
-

Four episodes in and I’m loving this show. The no-desk thing threw me off at first — the staging has a Tosh.0 vibe — but it works. The show has a fast pace and Bee jabs hard. A desk is leisurely, and Full Frontal is anything but.

- -
- - -
- - -
-
-The Talk Show: ‘Occupy Portland’  -
-
-

Special guest John Moltz returns to the show. Topics include the Apple/FBI encryption fight, Apple’s upcoming event and the products they’re expected to announce. And Campo Santo’s fantastic new video game Firewatch.

- -

Sponsored by:

- -
    -
  • Backblaze: Online backup for $5/month. Native. Unlimited. Unthrottled. Uncomplicated.
  • -
  • Squarespace: Build it beautiful. Use code GRUBER for 10% off your first order.
  • -
  • MailRoute: Hosted spam and virus protection for email. Use this link for 10 percent off for the life of your account.
  • -
- -
- - -
-Fiat Chrysler CEO: Apple Should Not Try Making a Car on Its Own  -
-
-

Reuters:

- -
-

Speaking to journalists at the Geneva auto show, Marchionne said -there was sufficient capacity available among car makers to deal -with Apple’s requirements and it would make more sense for them to -partner with a car manufacturer rather than become an actor itself -in such a “complex business”.

-
- -

I can see it now: the Fiat Rokr.

- -
-

“If they have any urges to make a car, I’d advise them to lie down -and wait until the feeling passes,” Marchionne told journalists. -“Illnesses like this come and go, you will recover from them, -they’re not lethal.”

-
- -

They’ve struggled for a few years here, figuring out how to make a decent car. Phone guys are not going to just figure this out. They’re not going to just walk in.

- -
- - -
-David Sparks on Apple Notes  -
-
-

David Sparks:

- -
-

Then I stated using Apple Notes and the strangest thing happened. -I liked it. Not only is Apple Notes a contender, Apple has -continued to refine the product. Just last week we got a new beta -of an upcoming Mac OS X release that includes additional Apple -Notes features. One of those new features is the ability to import -Evernote and plain text files. It seemed to me like a perfect -excuse to slurp in the rest of my nvALT database so I could really -push the application’s limits. Now I’ve got 787 notes in my Apple -Notes database. It’s growing daily.

- -

So first this was all a big experiment to see what was wrong with -Apple Notes and then I just started using the application. I -didn’t admit to myself, or anyone else, that I become an Apple -Notes user but apparently I have.

-
- -

Now that its syncing is based on CloudKit instead of IMAP, and with the features that have been added in iOS 9 and Mac OS X 10.11, Apple Notes is a solid notes app. I echo Sparks on this complaint:

- -
-

That doesn’t mean Apple Notes is without fault. I wrote before, -and it still remains true, that the text size on the Mac version -is just too small. They keep adding new features with the betas -and it keeps amazing me that they don’t address this problem.

-
- -

It’s almost mind boggling that you can’t change the default font size on the Mac. It’s not just a matter of preference, it’s a genuine accessibility problem. You can hit ⌘+ to increase the font size of the current note, but there’s no way to change the default for new notes. So if Helvetica 12 is too small for you to read, you’re stuck hitting ⌘+ for every single note.

- -

Update: And why is it Helvetica instead of San Francisco? Does the Apple Notes Mac team live in a cave?

- -
- - -
-A Complete History of the Millennium Falcon  -
-
-

Michael Heilemann, writing for his remarkable Kitbashed:

- -
-

One of the things I find so interesting about Star Wars is how the -creative process so clearly wasn’t locked from the beginning. It -was a long and winding road, and throughout writing the essays for -Kitbashed I’ve found that despite intense pressure there was -always an energetic adventurousness with ideas which inevitably -lead to some of the most iconic designs in film history.

- -

The Falcon is a great example of that, specifically because the -final design is so distinct. It makes it a much more enticing to -try to decipher how it came about.

- -

While I’ve been pursuing this subject for years, it wasn’t until I -starting putting together this essay that I finally began to find -some of the finer details of the Falcon’s creation.

-
- -

The Millennium Falcon is my favorite thing in all of Star Wars — and it was almost something altogether different. The original design was a fine space ship, but it had none of the character the actual Falcon has.

- -
- - -
-Former Google CEO Schmidt to Head New Pentagon Innovation Board  -
-
-

Andrea Shalal, reporting for Reuters:

- -
-

Eric Schmidt, the former chief executive officer of Google, will -head a new Pentagon advisory board aimed at bringing Silicon -Valley innovation and best practices to the U.S. military, Defense -Secretary Ash Carter said on Wednesday. Carter unveiled the new -Defense Innovation Advisory Board with Schmidt during the annual -RSA cyber security conference in San Francisco, saying it would -give the Pentagon access to “the brightest technical minds focused -on innovation.”

- -

Schmidt, now the executive chairman of Alphabet Inc (GOOGL.O), the -parent company of Google, said the board would help bridge what he -called a clear gap between how the U.S. military and the -technology industry operate.

-
- -

Coming soon to a headline near you: Google to Become Major Defense Contractor.

- -
- - -
-A Procastinator on Doing a TED Talk About Procrastination  -
-
-

Tim Urban:

- -
-

All TED speakers do a fully mic’ed and dressed rehearsal on the -real stage the weekend before the conference starts. Mine was -three days before my talk — and it was pretty rough, confirming -to me and everyone present that I was officially not a fraud when -it came to my topic. The irony of a guy rehearsing his TED Talk -about how he’s a bad procrastinator, and being clearly -underprepared while doing so, was not lost on anyone.

-
- -

This whole thing was white-knuckle reading for me.

- -
- - -
-‘Warrant-Proof Places’  -
-
-

From The Financial Times’s report on yesterday’s Apple/FBI hearing before Congress:

- -
-

“Our job is simply to tell people there is a problem,” Mr Comey -said. “If there are warrant-proof spaces in American life, what -does that mean and what are the costs of that?” He added: “The -tools we use to keep you safe are becoming less and less -effective.”

-
- -

There have always been “warrant-proof places” containing information inaccessible to law enforcement: our minds. I support the right to use unbreakable encryption for the same reason I support Fourth and Fifth Amendment rights, especially the right to remain silent.

- -
- - -
-Motherboard: ‘The Apple-FBI Encryption Hearing Was Unexpectedly Hostile to the FBI Director’  -
-
-

Sarah Jeong, reporting for Motherboard:

- -
-

A couple of representatives were openly hostile to Comey, but most -launched passive aggressive, loaded questions at the FBI director. -Even though the representatives (both Democrats and Republicans) -were mostly polite, the tone of the the questioning was a huge -departure from how the House Judiciary Committee typically -addresses Comey.

- -

“I would be deeply disappointed if it turns out the government is -found to be exploiting a national tragedy to pursue a change in -the law,” Rep. John Conyers (D-MI) told Comey. […] The questions -got more hostile. Rep. Conyers asked Comey if the San Bernardino -case was an “end-run around this committee” — a loaded question -that Comey of course denied. […]

- -

After that, Rep. Darrell Issa (R-CA) opened his questioning by -quoting the late Justice Antonin Scalia: “There is nothing new in -the realization that the Constitution sometimes insulates the -criminality of a few in order to protect the privacy of all of -us.” Issa’s questioning was overtly hostile in tone, delving deep -into the technical details of the iPhone 5c. Comey was at loss, -admitting, “I have not answered the questions you have asked me -today and I am not entirely sure I understand the questions.”

- -

Rep. Zoe Lofgren (D-CA) then said to Comey, “As I was hearing your -opening statement talking about a world where everything is -private, it may be the alternative is a world where nothing is -private. Because once you have holes in encryption, the question -is not if but when those holes will be exploited.”

-
- -

I’m actually not surprised at the hostility toward Comey. Democrats tend to support civil liberties against overreach from law enforcement, and Republicans — especially those in today’s House of Representatives — are extremely skeptical of an ever-more-powerful federal government. And both Republicans and Democrats yesterday seemed aware that the FBI’s use of the All Writs Act is, as Conyers put it, “an end-run around” Congress.

- -

If there’s one thing that can unite both parties in today’s polarized Congress, it is the protection of congressional authority. The idea that the Department of Justice (which is part of the Executive Branch) and the Judicial Branch could dictate the terms of this debate is not going to fly.

- -

Update: To be clear, there was also hostility toward Apple. That was expected by everyone. Some congresspeople are card-carrying members of the Golden Key Wizard Society.

- -
- - -
-February 2016 Was Very Warm  -
-
-

Eric Holthaus, writing for Slate:

- -
-

Keep in mind that it took from the dawn of the industrial age -until last October to reach the first 1.0 degree Celsius, and -we’ve come as much as an extra 0.4 degrees further in just the -last five months. Even accounting for the margin of error -associated with these preliminary datasets, that means it’s -virtually certain that February handily beat the record set just -last month for the most anomalously warm month ever recorded. -That’s stunning.

-
- -

February is typically brutally cold here in Philadelphia. It’s the month when I question why the hell I live here. This year, we had eight days with a high temperature in the 60s, and another four in the 50s. There were only four days where the temperature didn’t rise above freezing.

- -
- - -
- - -
-
-Silvia Killingsworth Takes Over The Awl  -
-
-

The Awl:

- -
-

We’re thrilled to announce that Silvia Killingsworth will be -joining us here in April. Silvia is currently the Managing Editor -of the New Yorker, where she has spent the last seven years -managing the workflow of the magazine. (You may also know her from -the web’s greatest food vertical, De Gustibus.) Silvia’s -breadth of experience and wealth of ideas and just genuine -enthusiasm (an emotion you may have noticed as being in short -supply over the last, say, seven years here) about things make her -the clear and obvious choice to head The Awl as it evolves into -its next stage of life.

-
- -

Great hire. Go Awl.

- -

Update: And in more media industry news, Nick Bilton has left The New York Times to become a special correspondent for Vanity Fair. Another great hire.

- -
- - -
-The Q4 2015 Smartphone Scorecard: Apple Gazes Down at the Rat Trap  -
-
-

Smart piece from Charles Arthur on the state of the handset industry.

- -
- - -
-Steve Ballmer Dunks During Clippers Halftime Show  -
-
-

Not sure about the Clips’ new mascot, though.

- -
- - -
- - -
-
-Apple Wins Major Court Victory Against FBI in a Case Similar to San Bernardino  -
-
-

Glenn Greenwald and Jenna McLaughlin, reporting for The Intercept:

- -
-

Judge Orenstein applied previous legal decisions interpreting the -AWA and concluded that the law does not “justif[y] imposing on -Apple the obligation to assist the government’s investigation -against its will.” In a formulation extremely favorable to Apple, -the judge wrote that the key question raised by the government’s -request is whether the AWA allows a court “to compel Apple — a -private party with no alleged involvement in Feng’s criminal -activity — to perform work for the government against its will.”

- -

The court ruled that the law permits no such result — both -because relevant law contains limits on what companies like Apple -are required to do, and because Congress never enacted any such -obligations. Moreover, the judge said of the government’s -arguments for how the AWA should be applied: “The implications of -the government’s position are so far-reaching — both in terms of -what it would allow today and what it implies about congressional -intent in 1789 — as to produce impermissibly absurd results.”

-
- -

This seems like great news for Apple and supporters of civil liberties in this case.

- -

Read Orenstein’s decision here.

- -
- - -
-San Bernardino Survivor’s Husband to Judge: Terrorist iPhone ‘Unlikely’ to Hold Valuable Information  -
-
-

I hope I don’t have to keep repeating this, but this is the wrong argument to make. The implication is that the result should be different if the iPhone in question was “likely” to contain valuable information. That’s wrong. Civil liberties apply equally in all situations.

- -

Don’t get me wrong — I’m glad they’re saying this particular iPhone is unlikely to actually contain useful information. But someday there will be a locked iPhone that is either likely or certain to contain useful information.

- -
- - -
-Fred Wilson: ‘The Twitter Contradiction’  -
-
-

Fred Wilson:

- -
-

I just don’t understand the narrative around Twitter. “It is in -trouble. It isn’t growing. It’s time has come and gone. The kids -all use Snapchat and Instagram.”

- -

That last part is true, to a degree. But it isn’t as simple -as that.

- -

The presumptive Republican nominee for President of the United -States has largely conducted his campaign on Twitter and in -massive public appearances that feel like rock concerts. He has -avoided the traditional media channels and taken his message -direct to the people on Twitter. Not on Facebook. Not on -Instagram. Not on Snapchat. Not on Pinterest. Not on his website -or mobile app. On Twitter.

-
- -

He makes a good point, but I don’t think there’s a contradiction. On the one hand, Twitter is a powerful publishing platform that has become the de facto official medium for famous people to make public statements about what is going on right now.

- -

The problem is, that’s not the description of a social network. It’s a description of a publishing platform. Twitter’s trouble is that it’s being viewed by investors as a social network.

- -
- - -
-Getting Called Up From the Big Leagues  -
-
-

M.G. Siegler, on Bill Simmons putting his new publication, The Ringer, on Medium:

- -
-

In a way, it almost feels like the thing to do now is the opposite -of what is typical in professional sports. In most leagues, -athletes play in minor leagues (or college) before graduating up -to the big leagues. In our new era of publishing, writers may -start at the big leagues, building up their skills and brands, -before venturing out on their own (or with a group of peers).

-
- -
- - -
-The Washington Post on Industry Support for Apple in Encryption Fight  -
-
-

Ellen Nakashima, writing for The Washington Post:

- -
-

Former Justice Department official Jennifer Daskal said both sides -are overstating their arguments. “The government is wrong to say -this is just about one case,” said Daskal, a law professor at -American University. “On the other hand, it is wrong to say that -if Apple loses this case, there’s absolutely no limits to what the -government can order a company to do” in cases involving encrypted -communications.

-
- -

This is false equivalence. The government really is wrong about this case being about just this one particular phone. But nobody (and certainly not Apple) is using words like “absolutely no limits to what the government can order a company to do” to describe what will happen if the government wins and sets precedent. The results will be significant, and I think chilling — but not limitless. This is just a bullshit quote to make the story sound “balanced”.

- -
-

One argument that companies and civil liberties groups are -expected to make is that if the government’s order is upheld, -then the FBI might be able to order a technology firm to create, -say, malicious software to send to a user’s device in the form of -a routine update. “That is the third rail for tech companies — -to be forced to deliver a software update that breaks the -security of the device,” said Alex Abdo, a staff attorney for the -American Civil Liberties Union, which is also filing a brief in -support of Apple.

-
- -

This would be one of the worst case scenarios I can imagine.

- -
- - -
-The State of Apple Music Connect  -
-
-

Dave Wiskus:

- -
-

If Connect is a social network, it fails miserably. There’s -nothing inherently social about the experience, which feels more -like a local bulletin board than a way for artists to engage -with fans.

- -

It’s also not a very good broadcast medium. Sure, I can post to -Connect and share out to Twitter and whatnot, but why? There’s -nothing unique or powerful about Apple’s system that makes it a -good hub. Because I have no idea how many followers we have, I -can’t even make a numerical argument for Connect-first posting. -And since we can’t even invite people from other places to follow -us on Connect, there’s no incentive to try.

- -

As a fan, it’s a confusing mess. As an artist, it’s a black hole. -All media, no social.

-
- -

Connect was a big part of the Apple Music introduction back in June, but I haven’t heard a word about it since other than when Dave writes about it.

- -
- - -
-Apple’s Statement to Congress on the FBI Warrant Fight  -
-
-

Apple general counsel Bruce Sewell testifies before Congress tomorrow. From his prepared opening statement:

- -
-

As we have told them — and as we have told the American public — -building that software tool would not affect just one iPhone. It -would weaken the security for all of them. In fact, just last week -Director Comey agreed that the FBI would likely use this precedent -in other cases involving other phones. District Attorney Vance has -also said he would absolutely plan to use this on over 175 phones. -We can all agree this is not about access to just one iPhone.

- -

The FBI is asking Apple to weaken the security of our products. -Hackers and cyber criminals could use this to wreak havoc on our -privacy and personal safety. It would set a dangerous precedent -for government intrusion on the privacy and safety of its -citizens.

-
- -
- - -
-It Doesn’t Matter Whether the San Bernardino iPhone Contains Useful Information  -
-
-

Jeff Gamet, writing for The Mac Observer:

- -
-

The iPhone recovered from Syed Farook after he shot and killed 14 -coworkers and then died in a shootout with police most likely -doesn’t hold any valuable information. So says San Bernardino -police chief Jarrod Burguan. Chief Burguan was asked about the -phone during an NPR interview and he replied:

- -
-

I’ll be honest with you, I think that there is a reasonably good -chance that there is nothing of any value on the phone. What we -are hoping might be on the phone would be potential contacts that -we would obviously want to talk to.

-
-
- -

There’s a small point to be made here, insofar as it suggests the FBI is being disingenuous. They’re saying that it’s not about precedent, it’s just about this one phone, this one investigation. But the real reason they’re making a big deal out of it is that it’s politically useful. The phone itself likely isn’t important but the situation surrounding the phone — “terrorism” and the tragedy of 14 innocent people being killed — lends sympathy to their desire for access to encrypted devices all the time.

- -

But for those of us on Apple’s side, this is not a point to hang our hats on. Even if law enforcement claimed to know with certainty that the phone contained useful information, Apple’s arguments would all still stand. Eventually there will be such a phone.

- -

And, likewise, I’m glad law enforcement is doing their best to check the contents of the phone. We want law enforcement to pursue all leads — within the confines of the law — even those that are unlikely to produce useful information.

- -
- - -
-Dan Frommer Named Editor in Chief at Recode  -
-
-

Kara Swisher:

- -
-

As Re/code has grown and morphed, we have always been on the -lookout for great talent to take the site to a new level.

- -

That’s why I’m very excited to announce that we’ve hired Dan -Frommer as the new editor in chief of Re/code. Dan brings our site -the energy, curiosity and tech-savvy we need to succeed in digital -publishing, an industry that gets more exciting — and challenging -— daily.

-
- -

Congratulations, pal.

- -
- - -
- - -
-
-Apple Product Event: Monday March 21  -
-
-

Kara Swisher, writing at Recode, broke the news:

- -
-

Attention Apple nerds, investors, media and everyone else who -needs to know when Tim Cook’s next product event is going to be -held: It’s going to be the week of March 21.

- -

Or to put it another way, it’s not going to be on March 15, the -time frame that other outlets previously reported, according to -several sources. It is not clear if the event was moved or if this -was the same timing as Apple had always planned.

-
- -

Swisher doesn’t have the exact date, although the <title> tag on her story reads “Apple Product Event Will Be Held March 22”. John Paczkowski (who usually gets these leaks first), confirms the week change, and says the event will be on Monday 21 March:

- -
-

Sources in position to know say the company has settled on March -21st as the date it will show off a handful of new products. These -people declined to say why Apple postponed the date by a week, but -it’s worth noting that it is one day prior to the company’s March -22 showdown with the government over a motion to compel it to help -hack the iPhone used by one of the San Bernardino terrorists.

-
- -

For what it’s worth, last year’s March event was on a Monday as well.

- -

Update: Jim Dalrymple:

- -
-

This sounds right to me.

-
- -
- - -
- - -
-
-Manuscripts and Findings  -
-
-

My thanks to Nucleobytes for sponsoring this week’s DF RSS feed. Nucleobytes is a fascinating company. They specialize in creating Mac and iOS software for scientists and researchers, and they do it with great style — their apps have won multiple Apple Design Awards.

- -

Their latest creations are two apps for researchers, useful for anyone who researches anything from lab results, cooking recipes, or research for blog posts: Manuscripts and Findings.

- -
    -
  • Manuscripts is a writing tool that helps you concentrate on your story. Outline, plan and edit your project, insert figures, tables and math, then format citations using a killer workflow. Manuscripts supports both importing and exporting Markdown, Word, LaTeX, and HTML.

  • -
  • Findings is a lab notebook app that helps you keep a journal of your research, connected to notes, photos, and files. Plan your week, track progress, and share your findings with your colleagues or the world.

  • -
- -

Try the free basic versions, and use coupon DARINGFIREBALL for a special discount on the unlimited versions, this week only. (They have an even better offer for students.)

- -
- - -
-Donald Trump Vows to ‘Open Up’ Libel Laws  -
-
-

Hadas Gold, writing for Politico:

- -
-

During a rally in Fort Worth, Texas, Trump began his usual tirade -against newspapers such as The New York Times and The Washington -Post, saying they’re “losing money” and are “dishonest.” The -Republican presidential candidate then took a different turn, -suggesting that when he’s president they’ll “have problems.”

- -

“One of the things I’m going to do if I win, and I hope we do and -we’re certainly leading. I’m going to open up our libel laws so -when they write purposely negative and horrible and false -articles, we can sue them and win lots of money. We’re going to -open up those libel laws. So when The New York Times writes a hit -piece which is a total disgrace or when The Washington Post, which -is there for other reasons, writes a hit piece, we can sue them -and win money instead of having no chance of winning because -they’re totally protected,” Trump said.

-
- -

Not worrisome at all. No sir.

- -
- - -
-Most Android Phones Are Not Encrypted  -
-
-

Jose Pagliery, writing for CNN Money:

- -
-

Although 97% of Android phones have encryption as an option, less -than 35% of them actually got prompted to turn it on when they -first activated the phone. Even then, not everybody chooses that -extra layer of security.

- -

A Google spokesman said that encryption is now required for all -“high-performing devices” — like the Galaxy S7 — running the -latest version of Android, Marshmallow. But only 1.2% of Android -phones even have that version, according to Google.

- -

By comparison, most Apple products are uniformly secure: 94% of -iPhones run iOS 8 or 9, which encrypt all data. Apple (AAPL, -Tech30) makes its devices, designs the software, and retains full -control of the phone’s operating system.

- -

“If a person walks into a Best Buy and walks out with an iPhone, -it’s encrypted by default. If they walk out with an Android phone, -it’s largely vulnerable to surveillance,” said Christopher -Soghoian, the principal technologist at the American Civil -Liberties Union.

-
- -

Google is moving in the right direction, but here’s an area where the slow uptake of new versions of Android has a serious effect.

- -
- - -
-9to5Mac: ‘Apple Likely to Drop the “5”, Call New 4-Inch Model the “iPhone SE”’  -
-
-

Mark Gurman:

- -
-

In January, we reported that Apple is preparing a new 4-inch -iPhone that is essentially 2013’s iPhone 5s with upgraded -internals. At the time, we heard that Apple would call the device -the “iPhone 5se” based on it being both an enhanced and “special -edition” version of the iPhone 5s. Now, we are hearing that Apple -appears to be going all in on the special edition factor: sources -say that Apple has decided to drop the “5” from the device’s name -and simply call it the “iPhone SE.” This will mark the first -iPhone upgrade without a number in its name and would logically -remove it from a yearly update cycle.

-
- -

A few points:

- -
    -
  • Apple was never going to call this phone the “5 SE”. I don’t know where Gurman got that, but that was never going to happen. Why would Apple give a new phone a name that makes it sound old?

  • -
  • Isn’t it more accurate to think of this as an iPhone 6S in a 4-inch body than as an iPhone 5S with “upgraded internals”? Other than the display, aren’t the “internals” the defining characteristics of any iPhone?

  • -
  • Dropping the number entirely fits with my theory that this phone is intended to remain on the market for 18-24 months.

  • -
- -
- - -
-Gogo Wi-Fi and Email Security  -
-
-

Reporter Steven Petrow published a scary first-hand tale in USA Today, claiming that his email was hacked by another passenger on a Gogo-enabled flight. The implication was that you shouldn’t use email on Gogo unless you’re using a VPN.

- -

But Petrow’s email didn’t get intercepted because of some flaw with Gogo. It got intercepted because he wasn’t connecting to the POP or SMTP servers via SSL. In fact, his email provider, Earthlink, doesn’t even support SSL for email.

- -

Robert Graham at Errata Security explains:

- -
-

Early Internet stuff wasn’t encrypted, because encryption was -hard, and it was hard for bad guys to tap into wires to eavesdrop. -Now, with open WiFi hotspots at Starbucks or on the airplane, it’s -easy for hackers to eavesdrop on your network traffic. -Simultaneously, encryption has become a lot easier. All new -companies, those still fighting to acquire new customers, have -thus upgraded their infrastructure to support encryption. Stagnant -old companies, who are just milking their customers for profits, -haven’t upgraded their infrastructure.

- -

You see this in the picture below. Earthlink supports older -un-encrypted “POP3” (for fetching email from the server), but not -the new encrypted POP3 over SSL. Conversely, GMail doesn’t support -the older un-encrypted stuff (even if you wanted it to), but only -the newer encrypted version.

-
- -

Gogo is far from perfect, but it certainly wasn’t at fault in this case.

- -

Update: Like a lot of you, I’m not even sure I buy the whole story. Whole thing seems fishy.

- -
- - -
- - -
-
-Google, Facebook, Twitter, and Microsoft Plan to Support Apple  -
-
-

Deepa Seetharaman and Jack Nicas, reporting for the WSJ:

- -
-

Several tech companies, including Google parent Alphabet Inc., -Facebook Inc. and Microsoft Corp., plan to file a joint motion -supporting Apple Inc. in its court fight against the Justice -Department over unlocking an alleged terrorist’s iPhone, according -to people familiar with the companies’ plans.

- -

At least one other tech company plans to be included in a joint -amicus brief next week generally supporting Apple’s position that -unlocking the iPhone would undermine tech firms’ efforts to -protect their users’ digital security, these people said. Twitter -Inc. also plans to support Apple in a motion, though it is unclear -if it will join the combined filing, another person familiar said.

- -

Microsoft President and Chief Legal Officer Brad Smith told -Congress on Thursday that his company would file a motion -supporting Apple.

-
- -

Nice.

- -
- - -
-Apple’s Motion to Vacate FBI Order  -
-
-

A clear, cogent read. I often shy away from reading legal motions because they’re so often written in dense legalese, but this one is clear.

- -

This stuck out to me:

- -
-

Congress knows how to impose a duty on third parties to facilitate -the government’s decryption of devices. Similarly, it knows -exactly how to place limits on what the government can require of -telecommunications carriers and also on manufacturers of telephone -equipment and handsets. And in CALEA, Congress decided not to -require electronic communication service providers, like Apple, to -do what the government seeks here. Contrary to the government’s -contention that CALEA is inapplicable to this dispute, Congress -declared via CALEA that the government cannot dictate to providers -of electronic communications services or manufacturers of -telecommunications equipment any specific equipment design or -software configuration.

- -

In the section of CALEA entitled “Design of features and systems -configurations,” 47 U.S.C. § 1002(b)(1), the statute says that it -“does not authorize any law enforcement agency or officer —

- -
-

(1) to require any specific design of equipment, facilities, - services, features, or system configurations to be adopted by - any provider of a wire or electronic communication service, - any manufacturer of telecommunications equipment, or any - provider of telecommunications support services.

- -

(2) to prohibit the adoption of any equipment, facility, service, - or feature by any provider of a wire or electronic - communication service, any manufacturer of telecommunications - equipment, or any provider of telecommunications support - services.

-
-
- -

What Apple is arguing is that the All Writs Act is intended only to fill the gaps covering scenarios not covered by other laws, but CALEA (the Communications Assistance for Law Enforcement Act) is a law that was passed specifically to cover exactly this sort of scenario. This strikes me as a very compelling argument.

- -
- - -
-Microsoft Will File Amicus Brief Supporting Apple  -
-
-

Dina Bass, reporting for Bloomberg:

- -
-

Microsoft Corp. backs Apple Inc. in its fight with the U.S. -government over unlocking a terrorist’s iPhone, said President and -Chief Legal Officer Brad Smith.

- -

The company will file an amicus brief to support Apple next week, -Smith said at a congressional hearing to discuss the need for new -legislation to govern privacy, security and law enforcement in the -age of Internet-based cloud services.

-
- -

Nice.

- -
- - -
-Apple to Tighten iCloud Backup Encryption  -
-
-

Tim Bradshaw, reporting for the Financial Times:

- -
-

Apple is working on new ways to strengthen the encryption of -customers’ iCloud backups in a way that would make it impossible -for the company to comply with valid requests for data from law -enforcement, according to people familiar with its plans.

- -

The move would bolster Apple customers’ security against hackers -but also frustrate investigators who are currently able to obtain -data from Apple’s servers through a court order. Apple has -complied with thousands of such orders in the past.

- -

Developing such technology is in some ways more complex than -adding the kind of device-level security that Apple introduced to -the iPhone in 2014 with its iOS 8 update.

- -

Building new protections that mean Apple no longer has access to -iCloud encryption keys may inconvenience some customers. Such a -change would most likely mean that customers who forget their -iCloud password may be left unable to access their photos, -contacts and other personal information that is backed up to -Apple’s systems.

-
- -
- - -
-The Dangerous All Writs Act Precedent in the Apple Encryption Case  -
-
-

Amy Davidson, writing for The New Yorker:

- -
-

It is essential to this story that the order to Apple is not a -subpoena: it is issued under the All Writs Act of 1789, which says -that federal courts can issue “all writs necessary or appropriate -in aid of their respective jurisdictions and agreeable to the -usages and principles of law.” Read as a whole, this simply means -that judges can tell people to follow the law, but they have to do -so in a way that, in itself, respects the law. The Act was written -at a time when a lot of the mechanics of the law still had to be -worked out. But there are qualifications there: warnings about the -writs having to be “appropriate” and “agreeable,” not just to the -law but to the law’s “principles.” The government, in its use of -the writ now, seems to be treating those caveats as background -noise. If it can tell Apple, which has been accused of no -wrongdoing, to sit down and write a custom operating system for -it, what else could it do?

-
- -

Lost amid the technical debate over encryption is the legal debate over this incredibly broad application of the All Writs Act.

- -
- - -
-Twitter’s Missing Manual  -
-
-

Eevee:

- -
-

Here, then, is a list of all the non-obvious things about Twitter -that I know. Consider it both a reference for people who aren’t up -to their eyeballs in Twitter, and an example of how these hidden -features can pile up. I’m also throwing in a couple notes on -etiquette, because I think that’s strongly informed by the shape -of the platform.

-
- -
- - -
-Sharp Accepts Foxconn Takeover Bid  -
-
-

Huge news for both companies. Interesting for Apple, too.

- -

Update:

- -
-

A deal to take over Japanese electronics giant Sharp by Taiwanese -manufacturer Foxconn, has been thrown into question by a last -minute delay.

- -

Foxconn said it had received new information from Sharp which -needed to be clarified.

-
- -

Whoops.

- -
- - -
- -
- - -
-

On the San Bernardino Suspect’s Apple ID Password Reset

- - -

The latest news in the Apple-FBI legal fight has resulted in much confusion. John Paczkowski, reporting for BuzzFeed:

- -
-

The FBI has claimed that the password was changed by someone at -the San Bernardino Health Department. Friday night, however, -things took a further turn when the San Bernardino County’s -official Twitter account stated, “The County was working -cooperatively with the FBI when it reset the iCloud password at -the FBI’s request.”

- -

County spokesman David Wert told BuzzFeed News on Saturday -afternoon the tweet was an authentic statement, but he had nothing -further to add.

- -

The Justice Department did not respond to requests for comment on -Saturday; an Apple spokesperson said the company had no additional -comment beyond prior statements.

-
- -

Here is what the FBI wrote in its legal motion, in a footnote on the four ways Apple suggested they obtain the data they seek:

- -
-

(3) to attempt an auto-backup of the SUBJECT DEVICE with the - related iCloud account (which would not work in this case - because neither the owner nor the government knew the password - the iCloud account, and the owner, in an attempt to gain - access to some information in the hours after the attack, was - able to reset the password remotely, but that had the effect - of eliminating the possibility of an auto-backup);

-
- -

To unpack this, the “owner” is not Syed Farook, the shooter. The iPhone at the center of this was supplied by Farook’s employer, the San Bernardino County Department of Public Health. They are the “owner”. The “government” is the federal government: the FBI and the Department of Justice.

- -

The iPhone had been configured to back up to iCloud. However, at the time of the attack, it had not been backed up to iCloud for six weeks. Under warrant, Apple supplied the FBI with the data from that six-week-old backup. The FBI (for obvious reasons) would like the most recent six weeks of data from the phone, too.1

- -

iCloud backups are triggered automatically when the phone is (a) on a known Wi-Fi network, and (b) plugged-in to power. Apple’s suggestion to the FBI was that if they took the iPhone to Farook’s office and plugged it in, it might trigger a backup. If that had worked, Apple could supply the FBI with the contents of that new backup, including the most recent six weeks of data.

- -

It is not clear to me from any of the reports I have read why the iPhone had not been backed up in six weeks. It’s possible that Farook had disabled iCloud backups, in which case this whole thing is moot.2 But it’s also possible the only reason the phone hadn’t been backed up in six weeks is that it had not been plugged-in while on a known Wi-Fi network in six weeks. The phone would have to be unlocked to determine this, and the whole point of this fight is that the phone can’t be unlocked.

- -

The FBI screwed this up by directing the San Bernardino County Department of Public Health to reset Farook’s Apple ID password. They did not, and apparently could not, change anything on the phone itself. But once they reset the Apple ID password, the phone could not back up to iCloud, because the phone needed to be updated with the newly-reset Apple ID password — and they could not do that because they can’t unlock the phone.

- -

The key point is that you do not have to unlock an iPhone to have it back up to iCloud. But a locked iPhone can’t back up to iCloud if the associated Apple ID password has been changed.

- -

Again, there are two password-type things at play here. The Apple ID (iCloud) password, and the four-digit device passcode locking the iPhone. The county, at the behest of the FBI, reset the Apple ID password. This did not allow them to unlock the iPhone, and, worse, it prevented the iPhone from initiating a new backup to iCloud.

- -

How did the county reset Farook’s Apple ID password? We don’t know for sure, but the most likely answer is that if his Apple ID was his work-issued email account, then the IT department at the county could go to iforgot.apple.com, enter Farook’s work email address, and then access his email account to click the confirmation URL to reset the password.

- -

In short:

- -
    -
  • The data the FBI claims to want is on Farook’s iPhone.
  • -
  • They already have access to his iCloud account.
  • -
  • They might have been able to transfer the data on his iPhone to his iCloud account via an automated backup, but they can’t because they reset his Apple ID (iCloud) password.
  • -
- -

The only possible explanations for this are incompetence or dishonesty on the part of the FBI. Incompetence, if they didn’t realize that resetting the Apple ID password could prevent the iPhone from backing up to iCloud. Dishonesty, if they directed the county to do this knowing the repercussions, with the goal of setting up this fight to force Apple to create a back door for them in iOS. I’m not sure which to believe at this point. I’d like to know exactly when this directive to reset the Apple ID password was given — ” in the hours after the attack” leaves a lot of wiggle room. 

- -
-
-
    -
  1. -

    Much (or all?) of the data stored on Apple’s iCloud backup servers is not encrypted. Or, if it is encrypted, it is encrypted in a way that Apple can decrypt. Apple has a PDF that describes the information available to U.S. law enforcement from iCloud, but to me it’s not clear exactly what is available under warrant. I would bet a large sum of money that Apple is hard at work on an iCloud backup system that does store data encrypted in a way that Apple cannot read it without the user’s Apple ID password. ↩︎

    -
  2. -
  3. -

    Another possibility: Farook’s iCloud storage was full. If this were the case, presumably Apple could have granted his account additional storage to allow a fresh backup to occur. But again, this became moot as soon as the county reset the Apple ID password at the behest of the FBI. ↩︎︎

    -
  4. -
-
- -
- -
- - -
-

Apple’s App Problem

- - -

Following up on Walt Mossberg’s column regarding the quality of Apple’s first-party apps, Jim Dalrymple writes:

- -
-

I understand that Apple has a lot of balls in the air, but they -have clearly taken their eye off some of them. There is absolutely -no doubt that Apple Music is getting better with each update to -the app, but what we have now is more of a 1.0 version than what -we received last year.

- -

Personally, I don’t care much about all the celebrities that Apple -can parade around — I care about a music service that works. -That’s it.

- -

If Apple Music (or any of the other software that has -problems) was the iPhone, it would never have been released in -the state it was.

-
- -

Software and hardware are profoundly different disciplines, so it’s hard to compare them directly. But it seems obvious to me that Apple, institutionally, has higher standards for hardware design and quality than it does for software.

- -

Maybe this is the natural result of the fact hardware standards must be high, because they can’t issue “hardware updates” over the air like they can with software. But the perception is now widespread that the balance between Apple’s hardware and software quality has shifted in recent years. I see a lot of people nodding their heads in agreement with Mossberg and Dalrymple’s pieces today.

- -

We went over this same ground a year ago in the wake of Marco Arment’s “Apple Has Lost the Functional High Ground”, culminating in a really interesting (to me at least) discussion with Phil Schiller at my “Live From WWDC” episode of The Talk Show. That we’re still talking about it a year later — and that the consensus reaction is one of agreement — suggests that Apple probably does have a software problem, and they definitely have a perception problem.

- -

I’ll offer a small personal anecdote. Overall I’ve had great success with iCloud Photo Library. I’ve got over 18,000 photos and almost 400 videos. And I’ve got a slew of devices — iPhones, iPads, and Macs — all using the same iCloud account. And those photos are available from all those devices. Except, a few weeks ago, I noticed that on my primary Mac, in Photos, at the bottom of the main “Photos” view, where it tells you exactly how many photos and videos you have, it said “Unable to Upload 5 Items”. Restarting didn’t fix it. Waiting didn’t fix it. And clicking on it didn’t do anything — I wanted to know which five items couldn’t be uploaded, and why. It seems to me that anybody in this situation would want to know those two things. But damned if Photos would tell me.

- -

Eventually, I found this support thread which suggested a solution: you can create a Smart Group in Photos using “Unable to upload to iCloud Photo Library” as the matching condition. Bingo: five items showed up. (Two of them were videos for which the original files couldn’t be found; three of them were duplicates of photos that were already in my library.)

- -

My little iCloud Photo Library syncing hiccup was not a huge deal — I was even lucky insofar as the two videos that couldn’t be found were meaningless. And I managed to find a solution. But it feels emblematic of the sort of nagging software problems people are struggling with in Apple’s apps. Not even the bug itself that led to these five items being unable to upload, but rather the fact that Photos knew about the problem but wouldn’t tell me the details I needed to fix it without my resorting to the very much non-obvious trick of creating a Smart Group to identify them. For me at least, “silent failure” is a big part of the problem — almost everything related to the whole discoveryd/mDNSresponder fiasco last year was about things that just silently stopped working.

- -

Maybe we expect too much from Apple’s software. But Apple’s hardware doesn’t have little problems like this. 

- -
- -
- - -
-

Why Apple Assembles in China

- - -

Arik Hesseldahl, writing for Recode on Donald Trump’s “we’re gonna get Apple to start building their damn computers and things in this country, instead of in other countries” campaign promise:

- -
-

Any honest presidential candidate regardless of party should say -clearly and indeed proudly that America doesn’t want these jobs to -come back. Final assembly jobs are low-skilled, low-paying -occupations; no American would wish to support a family on what -the jobs would pay. Workers at China’s Foxconn, which -manufacturers the iPhone, make about $402 per month after three -months of on-the-job probation. Even at the lowest minimum wage in -the U.S. — $5.15 an hour in Wyoming — American workers can’t -beat that.

-
- -

It’s not that simple. These jobs are certainly menial, but they’re not low-skill. As Tim Cook said on 60 Minutes:

- -
-

Charlie Rose: So if it’s not wages, what is it?

- -

Tim Cook: It’s skill. […]

- -

Charlie Rose: They have more skills than American workers? They -have more skills than —

- -

Tim Cook: Now — now, hold on.

- -

Charlie Rose: — German workers?

- -

Tim Cook: Yeah, let me — let me — let me clear, China put an -enormous focus on manufacturing. In what we would call, you and I -would call vocational kind of skills. The U.S., over time, began -to stop having as many vocational kind of skills. I mean, you can -take every tool and die maker in the United States and probably -put them in a room that we’re currently sitting in. In China, you -would have to have multiple football fields.

- -

Charlie Rose: Because they’ve taught those skills in their -schools?

- -

Tim Cook: It’s because it was a focus of them — it’s a focus of -their educational system. And so that is the reality.

-
- -

Wages are a huge factor, but for the sake of argument, let’s say Apple was willing to dip into its massive cash reserves and pay assembly line workers in the U.S. a good wage. Where would these U.S.-made iPhone be assembled? A year ago Apple sold 75 million iPhones in the fourth quarter of calendar 2014. There is no facility in the U.S. that can do that. There might not be anywhere in the world other than China that can operate at that sort of scale. That’s almost one million iPhones per day. 10 iPhones per second. Think about that.

- -

You can say, well, Apple could dig even deeper into its coffers and build such facilities. And train tens of thousands of employees. But why would they? Part of the marvel of Apple’s operations is that they can assemble and sell an unfathomable number of devices but they’re not on the hook for the assembly plants and facilities. When iPhones go the way of the iPod in 10 or 15 or 20 years, Apple doesn’t have any factories to close or convert for other uses. Foxconn does.

- -

The U.S. can’t compete with China on wages. It can’t compete on the size of the labor force. China has had a decades-long push in its education system to train these workers; the U.S. has not. And the U.S. doesn’t have the facilities or the proximity to the Asian component manufacturers.

- -

The only way Apple could ever switch to U.S. assembly and manufacturing would be if they automated the entire process — to build machines that build the machines. That, in fact, is what NeXT did while they were in the hardware business. But NeXT only ever sold about 50,000 computers total. Apple needed to assemble 35,000 iPhones per hour last year.

- -

So long as assembling these devices remains labor intensive, it has to happen in China. And if someday it becomes automated — if the machines are built by machines — by definition it’s not going to create manufacturing jobs.1 

- -
-
-
    -
  1. -

    I do wonder about the purported Apple car. Would that be assembled in China, too? The U.S. does have automobile manufacturing expertise. And a car is so utterly unlike any product Apple has ever made that I feel like anything is possible. ↩︎

    -
  2. -
-
- -
- -
- - - - -
- - -

-Ads via The Deck -Ads via The Deck -

- -
- - - - - - -
- -
- - - - diff --git a/Frameworks/RSParser/RSParserTests/Resources/DaringFireball.json b/Frameworks/RSParser/RSParserTests/Resources/DaringFireball.json deleted file mode 100644 index 613269e88..000000000 --- a/Frameworks/RSParser/RSParserTests/Resources/DaringFireball.json +++ /dev/null @@ -1,584 +0,0 @@ -{ - "version" : "https://jsonfeed.org/version/1", - "title" : "Daring Fireball", - "home_page_url" : "https://daringfireball.net/", - "feed_url" : "https://daringfireball.net/feeds/json", - "author" : { - "url" : "https://twitter.com/gruber", - "name" : "John Gruber" - }, - "icon" : "https://daringfireball.net/graphics/apple-touch-icon.png", - "favicon" : "https://daringfireball.net/graphics/favicon-64.png", - "items" : [ - { - "title" : "The Talk Show: ‘I Do Like Throwing a Baby’", - "date_published" : "2017-06-27T00:54:17Z", - "date_modified" : "2017-06-27T00:54:20Z", - "id" : "https://daringfireball.net/linked/2017/06/26/the-talk-show-195", - "url" : "https://daringfireball.net/linked/2017/06/26/the-talk-show-195", - "external_url" : "https://daringfireball.net/thetalkshow/2017/06/26/ep-195", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

New episode of America’s favorite 3-star podcast, with special guest John Moltz. Topics include more follow-up from WWDC 2017, the iPad Pro models and ProMotion, Scott Forstall’s interview with John Markoff regarding the 10-year anniversary of the original iPhone, the ongoing shitshow at Uber, quick thoughts on the Nintendo Switch, and more. Also: guess which John enjoys throwing babies into the air.

\n\n

Sponsored by:

\n\n
    \n
  • Squarespace: Make your next move. Use code GRUBER for 10% off your first order.
  • \n
  • Away: High-quality luggage with built-in USB chargers. Save $20 with promo code TALKSHOW.
  • \n
  • MailRoute: Hosted spam and virus protection for email. Use this link for 10% off for the life of your account.
  • \n
\n\n\n\n\t" - }, - { - "title" : "Virgin Mobile Partners With Apple to Go iPhone-Only With $1 Service", - "date_published" : "2017-06-26T23:51:11Z", - "date_modified" : "2017-06-26T23:51:13Z", - "id" : "https://daringfireball.net/linked/2017/06/26/virgin-mobile-iphone-only", - "url" : "https://daringfireball.net/linked/2017/06/26/virgin-mobile-iphone-only", - "external_url" : "http://tidbits.com/article/17308", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Josh Centers, writing for TidBITS on Virgin Mobile’s intriguing decision to go iPhone-only:

\n\n
\n

Pundits have long suspected that two roadblocks stood in the way\nof Apple becoming a carrier: the infrastructure is incredibly\nexpensive, even if you lease it from the larger carriers, and\nApple could limit the iPhone business if it were to compete with\nthe major carriers.

\n\n

But Apple has sidestepped those concerns by essentially taking\nover a carrier (actually a carrier-owned MVNO — Mobile Virtual\nNetwork Operator) without acquiring it. Apple may not own Virgin\nMobile, but Virgin Mobile is now utterly dependent on Apple and\nwill benefit through promotion in Apple Stores.

\n\n

We shouldn’t read too much into this deal, but at the very least\nit’s unusual to see a company like Virgin Mobile going all-in on\nthe iPhone. And it might point toward Apple dipping its toe into\nthe MVNO business.

\n
\n\n

Virgin Mobile is owned by Sprint (and thus uses Sprint’s back-end), and in my experience Sprint is the worst of the U.S. carriers, so this is not a panacea. But it is intriguing.

\n\n\n\n\t" - }, - { - "title" : "The Verge: ‘Apple’s AR Is Closer to Reality Than Google’s’", - "date_published" : "2017-06-26T23:02:21Z", - "date_modified" : "2017-06-26T23:02:23Z", - "id" : "https://daringfireball.net/linked/2017/06/26/arkit-the-verge", - "url" : "https://daringfireball.net/linked/2017/06/26/arkit-the-verge", - "external_url" : "https://www.theverge.com/2017/6/26/15872332/apple-arkit-ios-11-augmented-reality-developer-excitement", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Two great examples via the very fun Made With ARKit Twitter account: here and here.

\n\n\n\n\t" - }, - { - "title" : "Rene Ritchie’s First Look at the iOS 11 Public Beta", - "date_published" : "2017-06-26T22:55:13Z", - "date_modified" : "2017-06-26T23:13:17Z", - "id" : "https://daringfireball.net/linked/2017/06/26/ios-11-public-beta-ritchie", - "url" : "https://daringfireball.net/linked/2017/06/26/ios-11-public-beta-ritchie", - "external_url" : "https://www.imore.com/ios-11", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Rene Ritchie has a comprehensive look at the just-released public beta of iOS 11. Romain Dillet has a good preview at TechCrunch too. The gist of both previews: it’s the “I hope Apple truly focuses on the iPad this year” release of iOS that we’ve been waiting for.

\n\n

I’ve been using the developer betas on my 10.5-inch iPad Pro review unit and a spare iPhone. I’m willing to wait to install iOS 11 on my primary iPhone, but at this point, bugs be damned, I wouldn’t want to use an iPad running iOS 10.3. It’s stable enough, and the benefits of the great new features for iPad far outweigh the downsides of the beta (which, in addition to crashing bugs, include questionable battery life).

\n\n\n\n\t" - }, - { - "title" : "Amazon’s New Echo Show Is Very Cool and a Little Creepy", - "date_published" : "2017-06-26T22:08:09Z", - "date_modified" : "2017-06-26T23:18:49Z", - "id" : "https://daringfireball.net/linked/2017/06/26/echo-show-creep", - "url" : "https://daringfireball.net/linked/2017/06/26/echo-show-creep", - "external_url" : "https://www.buzzfeed.com/mathonan/meet-amazons-new-echo-show-alexa-is-watching", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Mat Honan, writing for BuzzFeed:

\n\n
\n

It has this wild new feature called Drop In. Drop In lets you give\npeople permission to automatically connect with your device.\nHere’s how it works. Let’s say my father has activated Drop In for\nme on his Echo Show. All I have to do is say, “Alexa, drop in on\nDad.” It then turns on the microphone and camera on my father’s\ndevice and starts broadcasting that to me. For the several seconds\nof the call, my father’s video screen would appear fogged over.\nBut then there he’ll be. And to be clear: This happens even if he\ndoesn’t answer. Unless he declines the call, audibly or by tapping\non the screen, it goes through. It just starts. Hello, you look\nnice today.

\n\n

Honestly, I haven’t figured out what to think about this yet. But,\nit’s here.

\n
\n\n

I know what to think of this: No fucking way do I want this.

\n\n

Update: I’ve already gotten a few reader responses arguing that this feature could be great for an Echo Show in the home of an elderly relative. You visit and set it up in their house, explain to them what it does, and then you can check in with them without their needing to do anything at all. I can see that. You can think of it as the digital equivalent of having a set of keys to someone’s house — something you’d only grant to a deeply trusted friend or loved one.

\n\n\n\n\t" - }, - { - "title" : "Verizon to Block Email Addresses From Rival Carriers From Logging Into Yahoo or Tumblr Accounts", - "date_published" : "2017-06-26T21:18:46Z", - "date_modified" : "2017-06-26T22:18:03Z", - "id" : "https://daringfireball.net/linked/2017/06/26/verizon-fuck-heads", - "url" : "https://daringfireball.net/linked/2017/06/26/verizon-fuck-heads", - "external_url" : "https://tumblr.zendesk.com/hc/en-us/articles/115007729788-Heads-up-for-AT-T-customers", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

From a Tumblr help document euphemistically titled “Heads-Up for AT&T Customers”:

\n\n
\n

Starting on June 30, 2017, att.net customers will no longer be\nable to log in to their Yahoo and Tumblr accounts through email\naddresses with the following domains: att.net, ameritech.net,\nbellsouth.net, flash.net, nvbell.net, pacbell.net, prodigy.net,\nsbcglobal.net, snet.net, swbell.net, and wans.net.

\n
\n\n

The sheer egregiousness of this is outrageous on its face, but it’s even worse when you consider that Tumblr, when it was independent, was a champion for net neutrality.

\n\n

Update: TechCrunch says it’s just a deal expiring, not spite:

\n\n
\n

As part of the new corporate merger of Yahoo and Aol under the\nOath brand, it looks like Yahoo accounts will no longer be\naccessible through AT&T email addresses (or those of any A&T\nsubsidiaries).

\n\n

The move provoked some uproar among net neutrality advocates, but\nit seems to be less about creating walled gardens and more about\ncleaning up prior commitments and pre-existing partnerships.\nWhile there is a level of inconvenience for AT&T customers, this\nis less about net neutrality and more about unwinding those\ncorporate deals.

\n
\n\n

I still say fuck Verizon and their stance on net neutrality.

\n\n\n\n\t" - }, - { - "title" : "Trump’s Lies, the Definitive List", - "date_published" : "2017-06-26T21:08:43Z", - "date_modified" : "2017-06-26T21:08:44Z", - "id" : "https://daringfireball.net/linked/2017/06/26/trumps-lies", - "url" : "https://daringfireball.net/linked/2017/06/26/trumps-lies", - "external_url" : "https://www.nytimes.com/interactive/2017/06/23/opinion/trumps-lies.html?_r=0", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Copiously documented and perfectly presented. Looked striking in the print edition, too.

\n\n\n\n\t" - }, - { - "title" : "[Sponsor] Timing — Automatic Time Tracking for Mac", - "date_published" : "2017-06-26T19:22:44-04:00", - "date_modified" : "2017-06-26T19:22:46-04:00", - "id" : "https://daringfireball.net/feeds/sponsors/2017/06/timing_automatic_time_tracking", - "url" : "https://daringfireball.net/feeds/sponsors/2017/06/timing_automatic_time_tracking", - "external_url" : "https://timingapp.com/df", - "author" : { - "name" : "Daring Fireball Department of Commerce" - }, - "content_html" : "\n

Time is your most precious resource. You need to know how you are spending it.
\nBut time tracking sucks. Big Time. (Pun intended.)

\n\n

The brand new Timing fixes that. It automatically tracks which apps, documents and websites you use — without start/stop timers. See how you spend your time, eliminate distracting activities, and improve your client billing. Mind you, this data is super sensitive, so Timing keeps it safe on your Mac.

\n\n

Stop worrying about time and focus on doing your best work instead.

\n\n

Download a free 14-day trial today and get 10 percent off through next Monday.

\n\n\n\n\t" - }, - { - "title" : "Delta Updates and App Thinning Do Not Solve the Apps-Are-Too-Damn-Big Problem", - "date_published" : "2017-06-25T15:31:10Z", - "date_modified" : "2017-06-25T15:31:12Z", - "id" : "https://daringfireball.net/linked/2017/06/25/delta-updates-birchler", - "url" : "https://daringfireball.net/linked/2017/06/25/delta-updates-birchler", - "external_url" : "https://birchtree.me/blog/the-apps-are-too-damn-big/", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Matt Birchler:

\n\n
\n

“App thinning” is not a magic bullet that erases this problem\nthough, as Facebook Messenger, which shows as being 154 MB, still\ndownloaded 99 MB of data for its update. […]

\n\n

So are giant app sizes a problem? Yes. Do delta updates allow\nthese updates to use less data? Yes. Do delta updates make these\nlarge apps a non-issue? Hell no!

\n
\n\n\n\n\t" - }, - { - "title" : "Mnml – A Mac Client for Medium", - "date_published" : "2017-06-25T00:14:25Z", - "date_modified" : "2017-06-25T02:42:27Z", - "id" : "https://daringfireball.net/linked/2017/06/24/mnml", - "url" : "https://daringfireball.net/linked/2017/06/24/mnml", - "external_url" : "http://mnml.af/", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

My thanks to Mnml for sponsoring this week’s DF RSS feed. It’s a native Mac app client for Medium, and can be used for writing, blogging, and notetaking. Based upon the same engine that twice won Desk “Best Apps of the Year” honors, Mnml has all the features you’ll need, wrapped in an attractive, fun – and yes, minimal – interface. Anyone who writes for Medium and prefers native client apps should check it out.

\n\n\n\n\t" - }, - { - "title" : "Scott Galloway on Amazon and Whole Foods", - "date_published" : "2017-06-23T18:45:50Z", - "date_modified" : "2017-06-23T19:23:34Z", - "id" : "https://daringfireball.net/linked/2017/06/23/galloway-amzn-wfm", - "url" : "https://daringfireball.net/linked/2017/06/23/galloway-amzn-wfm", - "external_url" : "https://www.l2inc.com/daily-insights/no-mercy-no-malice/amzn-wfm-1t", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Scott Galloway:

\n\n
\n

Amazon / Whole Foods will be the fourth-largest grocer in the US,\nand will likely post growth rates no $10B+ retailer, sans Amazon,\nhas registered. The Seattle firm will apply its operational chops\nand lower (zero) profit hurdle to the Whole Foods business model\nand bring prices (way) down. If you wish you could shop at Whole\nFoods more often, but it’s too expensive, your prayers have been\nanswered. Whole Foods will become the grocery equivalent of a\nMercedes for the price of a Toyota. Grocery has stuck their chin\nout (little innovation), and the entire sector is about to have\nits jaw shattered.

\n
\n\n

It’s a great piece. I disagree with him on this though:

\n\n
\n

Amazon will displace Apple as the top tech hardware innovator,\nwith Alexa cementing itself as the gadget that defines the decade\n(post iPhone). Grocery / commerce via Alexa will create the\nutility that Alexa needs to [maintain its lead] over Google and\nApple’s home / voice offerings as they try to play catch-up.

\n
\n\n

Alexa may well maintain its lead in the smart speaker market. It may even grow. Maybe HomePod will be a complete bust. But even if all of that happens, the smartphone will remain the dominant device in people’s lives. Something will eventually replace the phone, but smart speakers aren’t it.

\n\n

Hardware just isn’t where Amazon is good.

\n\n\n\n\t" - }, - { - "title" : "Google Will Stop Reading Your Emails for Gmail Ads", - "date_published" : "2017-06-23T18:32:03Z", - "date_modified" : "2017-06-23T18:32:05Z", - "id" : "https://daringfireball.net/linked/2017/06/23/gmail-privacy", - "url" : "https://daringfireball.net/linked/2017/06/23/gmail-privacy", - "external_url" : "https://www.bloomberg.com/news/articles/2017-06-23/google-will-stop-reading-your-emails-for-gmail-ads", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Mark Bergen, reporting for Bloomberg:

\n\n
\n

Google is stopping one of the most controversial advertising\nformats: ads inside Gmail that scan users’ email contents. The\ndecision didn’t come from Google’s ad team, but from its cloud\nunit, which is angling to sign up more corporate customers.

\n\n

Alphabet Inc.’s Google Cloud sells a package of office software,\ncalled G Suite, that competes with market leader Microsoft Corp.\nPaying Gmail users never received the email-scanning ads like the\nfree version of the program, but some business customers were\nconfused by the distinction and its privacy implications, said\nDiane Greene, Google’s senior vice president of cloud. “What we’re\ngoing to do is make it unambiguous,” she said.

\n
\n\n

This is terrific news. Not just because it’s a good policy change in and of itself, but I take it as a sign that Google’s leadership is starting to realize how much damage they’ve done to the company’s reputation by playing fast and loose with their users’ privacy.

\n\n\n\n\t" - }, - { - "title" : "Who Americans Spend Their Time With", - "date_published" : "2017-06-23T18:13:55Z", - "date_modified" : "2017-06-23T18:15:59Z", - "id" : "https://daringfireball.net/linked/2017/06/23/who-americans-spend-their-time-with", - "url" : "https://daringfireball.net/linked/2017/06/23/who-americans-spend-their-time-with", - "external_url" : "https://www.theatlas.com/charts/HJFYm4uQ-", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Via Jim Coudal, who summarizes this perfectly: “Poetry, in data”.

\n\n\n\n\t" - }, - { - "title" : "Kevin Drum’s Thoughtful Critique of Trumpcare", - "date_published" : "2017-06-23T17:58:25Z", - "date_modified" : "2017-06-23T17:58:27Z", - "id" : "https://daringfireball.net/linked/2017/06/23/drum-trumpcare", - "url" : "https://daringfireball.net/linked/2017/06/23/drum-trumpcare", - "external_url" : "http://www.motherjones.com/kevin-drum/2017/06/my-thoughtful-critique-of-trumpcare/", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

A succinct rundown of what’s wrong with the Senate Republicans’ “health care” bill.

\n\n\n\n\t" - }, - { - "title" : "Nintendo: Switch Shortages Are ‘Definitely Not Intentional’", - "date_published" : "2017-06-23T17:46:13Z", - "date_modified" : "2017-06-23T17:46:15Z", - "id" : "https://daringfireball.net/linked/2017/06/23/nintendo-switch", - "url" : "https://daringfireball.net/linked/2017/06/23/nintendo-switch", - "external_url" : "https://arstechnica.com/gaming/2017/06/nintendo-switch-shortages-are-definitely-not-intentional/", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Kyle Orland, reporting for Ars Technica:

\n\n
\n

Since the days of the NES, people have accused Nintendo of\nintentionally underproducing hardware in order to drive an\nartificial feeding frenzy of demand in the marketplace. With the\nNintendo Switch remaining nearly impossible to find at retailers\nnationwide, those same accusations of “false scarcity” have been\nbubbling up in certain corners.

\n\n

Nintendo Senior Director of Corporate Communications Charlie\nScibetta wants to push back on those accusations. “It’s definitely\nnot intentional in terms of shorting the market,” he told Ars in a\nrecent interview. “We’re making it as fast as we can. We want to\nget as many units out as we can to support all the software that’s\ncoming out right now… our job really is to get it out as quick\nas we can, especially for this holiday because we want to have\nunits on shelves to support Super Mario Odyssey.”

\n
\n\n\n\n\t" - }, - { - "title" : "How to Build Smaller Apps", - "date_published" : "2017-06-22T23:03:38Z", - "date_modified" : "2017-06-22T23:03:40Z", - "id" : "https://daringfireball.net/linked/2017/06/22/sandofsky-app-bloat", - "url" : "https://daringfireball.net/linked/2017/06/22/sandofsky-app-bloat", - "external_url" : "https://blog.halide.cam/one-weird-trick-to-lose-size-c0a4013de331", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Ben Sandofsky:

\n\n
\n

Popular social networking apps are over 400 megs. With weekly\nreleases, over one year you’ll download twenty gigs of data.

\n\n

Since we launched Halide, the most unexpected compliment we’ve\nheard is about its size. At 11 megs, we’ll push less data in one\nyear than a social network pushes in a single update.

\n\n

“So you aren’t using Swift,” asked a friend. After all, Swift\nbundles its standard libraries into your app, bloating its size.\nHalide is almost entirely Swift. How did we do it? Let’s start\nwith the technical bits.

\n
\n\n

His conclusion is spot-on:

\n\n
\n

There really is one weird trick to lose size: focus on your customers.

\n
\n\n\n\n\t" - }, - { - "title" : "Bad App Citizens", - "date_published" : "2017-06-22T22:51:51Z", - "date_modified" : "2017-06-23T16:54:33Z", - "id" : "https://daringfireball.net/linked/2017/06/22/darke-bad-app-citizens", - "url" : "https://daringfireball.net/linked/2017/06/22/darke-bad-app-citizens", - "external_url" : "http://www.everyinteraction.com/blog/bad-app-citizens/", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Jon Darke:

\n\n
\n

This got me thinking — as a user who has a lot of apps\ninstalled, how much bandwidth does my phone use to keep my apps\nupdated? […]

\n\n

One Friday I turned off auto-update for apps and let the update\nqueue build up for a week. The results shocked me.

\n
\n\n

It’s getting to the point where most apps can’t be updated over cellular because they’re all over 100 MB. This is madness.

\n\n

Update: Many readers have written to argue that the listed sizes in the App Store aren’t what you actually download when updating an app, thanks to app thinning and other features. OK, but even with app thinning and delta updates these apps are still way too big as downloads and take up way too much storage on devices.

\n\n\n\n\t" - }, - { - "title" : "More Than 1,000 Uber Employees Ask for Travis Kalanick to Return", - "date_published" : "2017-06-22T22:24:24Z", - "date_modified" : "2017-06-22T22:24:26Z", - "id" : "https://daringfireball.net/linked/2017/06/22/uber-employees", - "url" : "https://daringfireball.net/linked/2017/06/22/uber-employees", - "external_url" : "https://www.axios.com/over-1-000-uber-employees-ask-for-travis-kalanick-to-return-2446313529.html", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Dan Primack, reporting for Axios:

\n\n
\n

More than one thousand current Uber employees have signed a letter\nto the company’s board of directors, asking for the return of\ndeposed CEO Travis Kalanick “in an operational role.” One of its\nventure capital investors also is chiming in, with a similar\nmessage.

\n
\n\n

Not surprising to me at all — Uber was made in Kalanick’s image.

\n\n\n\n\t" - }, - { - "title" : "Kara Swisher: ‘Susan Fowler Proved That One Person Can Make a Difference’", - "date_published" : "2017-06-22T20:15:16Z", - "date_modified" : "2017-06-22T20:15:18Z", - "id" : "https://daringfireball.net/linked/2017/06/22/fowler-swisher", - "url" : "https://daringfireball.net/linked/2017/06/22/fowler-swisher", - "external_url" : "https://www.recode.net/2017/6/21/15844852/uber-toxic-bro-company-culture-susan-fowler-blog-post", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Kara Swisher:

\n\n
\n

It was Lao Tzu who said that “the journey of a thousand miles\nbegins with a single step.”

\n\n

In the case of complete and utter change reeling through Uber\nright now — culminating in the resignation of its once\nuntouchable CEO Travis Kalanick — it turns out that it began with\none of the most epic blog posts to be written about what happens\nwhen a hot company becomes hostage to its increasingly\ndysfunctional and toxic behaviors.

\n\n

It was clear from the moment you read the 3,000-word post by\nformer engineer Susan Fowler about her time at the car-hailing\ncompany that nothing was going to be the same. Titled simply,\n“Reflecting on one very, very strange year at Uber,” the\nessay deftly and surgically laid out the map that the media and\nothers would use to prove to its out-to-lunch board and waffling\ninvestors that Uber CEO Travis Kalanick had to go.

\n
\n\n

The truth and courage are a powerful combination.

\n\n\n\n\t" - }, - { - "title" : "Gizmodo Investigation Exposes Websites Collecting Form Data Before You Hit ‘Submit’", - "date_published" : "2017-06-22T19:54:12Z", - "date_modified" : "2017-06-22T23:18:05Z", - "id" : "https://daringfireball.net/linked/2017/06/22/navistone-form-data", - "url" : "https://daringfireball.net/linked/2017/06/22/navistone-form-data", - "external_url" : "http://gizmodo.com/before-you-hit-submit-this-company-has-already-logge-1795906081", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Great investigative work by Kashmir Hill and Surya Mattu for Gizmodo:

\n\n
\n

During a recent investigation into how a drug-trial recruitment\ncompany called Acurian Health tracks down people who look online\nfor information about their medical conditions, we discovered\nNaviStone’s code on sites run by Acurian, Quicken Loans, a\ncontinuing education center, a clothing store for plus-sized\nwomen, and a host of other retailers. Using Javascript, those\nsites were transmitting information from people as soon as they\ntyped or auto-filled it into an online form. That way, the company\nwould have it even if those people immediately changed their minds\nand closed the page. […]

\n\n

We decided to test how the code works by pretending to shop on\nsites that use it and then browsing away without finalizing the\npurchase. Three sites — hardware site Rockler.com, gift site\nCollectionsEtc.com, and clothing site BostonProper.com — sent us\nemails about items we’d left in our shopping carts using the email\naddresses we’d typed onto the site but had not formally submitted.\nAlthough Gizmodo was able to see the email address information\nbeing sent to Navistone, the company said that it was not\nresponsible for those emails.

\n
\n\n

They weren’t responsible for sending the emails, but they were responsible for the email addresses being sent to those websites in the first place. Sending form data surreptitiously is morally wrong, and everyone knows it.

\n\n

This might sound hyperbolic, but I mean it: I think we’d be better off if JavaScript had never been added to web browsers.

\n\n\n\n\t" - }, - { - "title" : "Uber’s Biggest Problem: Its Business Model", - "date_published" : "2017-06-22T18:54:45Z", - "date_modified" : "2017-06-22T18:54:46Z", - "id" : "https://daringfireball.net/linked/2017/06/22/mims-uber", - "url" : "https://daringfireball.net/linked/2017/06/22/mims-uber", - "external_url" : "https://www.wsj.com/articles/with-kalanick-out-ubers-troubles-are-just-beginning-1498049054", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Christopher Mims, in his column for The Wall Street Journal:

\n\n
\n

But even when it steers through that thicket of crises, Uber will\nhave to come to grips with a fundamental vulnerability that is\nincreasingly apparent in the company’s business model. Uber may be\ngreat at technology, but unlike the businesses of Google,\nFacebook, Apple or Amazon, technology hasn’t proven to be a\nsignificant barrier to new entrants in ride-sharing. Across the\nglobe, Uber has dozens of competitors, and in many markets they\nhave grabbed the lion’s share of the ride-sharing market.

\n\n

Even if Uber fixes all of its current problems, it’s increasingly\nunlikely that it can live up to the inflated expectations that\ncome with the nearly $70 billion valuation that have made it the\nworld’s most valuable startup. There are barbarians at Uber’s\ngate, and it’s sorely in need of a moat.

\n
\n\n

This is why they’re pursuing self-driving technology so aggressively. There’s simply no way that Uber is worth $70 billion without some sort of exclusive technical advantage. That’s the interesting flip side to Kalanick’s ouster — I’m not sure who would want the job.

\n\n\n\n\t" - }, - { - "title" : "Chris Lattner on His Stint at Tesla", - "date_published" : "2017-06-22T18:26:01Z", - "date_modified" : "2017-06-22T19:13:09Z", - "id" : "https://daringfireball.net/linked/2017/06/22/lattner-resume", - "url" : "https://daringfireball.net/linked/2017/06/22/lattner-resume", - "external_url" : "http://www.nondot.org/sabre/Resume.html", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Chris Lattner has updated his resume with his accomplishments at Tesla. Unsurprisingly, it sounds like he got a lot done in just five months — including, ironically, addressing an engineering talent retention problem.

\n\n\n\n\t" - }, - { - "title" : "Inside Travis Kalanick’s Resignation", - "date_published" : "2017-06-22T18:07:14Z", - "date_modified" : "2017-06-22T18:07:15Z", - "id" : "https://daringfireball.net/linked/2017/06/22/kalanick-isaac", - "url" : "https://daringfireball.net/linked/2017/06/22/kalanick-isaac", - "external_url" : "https://www.nytimes.com/2017/06/21/technology/uber-travis-kalanick-final-hours.html", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Mike Isaac, reporting for The New York Times:

\n\n
\n

Travis Kalanick’s final hours as Uber’s chief executive played out\nin a private room in a downtown Chicago hotel on Tuesday.

\n\n

There, Mr. Kalanick, who was on a trip to interview executive\ncandidates for Uber, was paid a surprise visit. Two venture\ncapitalists — Matt Cohler and Peter Fenton of the Silicon Valley\nfirm Benchmark, which is one of Uber’s biggest shareholders —\npresented Mr. Kalanick with a list of demands, including his\nresignation before the end of the day. The letter was from five of\nUber’s major investors, including Benchmark and the mutual fund\ngiant Fidelity Investments. […]

\n\n

By the end of the day, after hours of haggling and arguing, that\ncourse was clear: Mr. Kalanick agreed to step down as Uber’s chief\nexecutive.

\n
\n\n

Truly great reporting from Isaac, including the fact that even during his brief “leave of absence”, he wasn’t really absent at all:

\n\n
\n

In reality, Mr. Kalanick had little intention of staying away from\nhis company. Almost immediately after announcing the leave of\nabsence, he worked the phones to push out Mr. Bonderman for making\nthe sexist comment onstage at an Uber employee meeting. With the\ntwo increasingly at odds, Mr. Kalanick sent out a flurry of texts,\nphone calls and emails to his allies to pressure Mr. Bonderman to\nstep down from Uber’s board. Hours later, Mr. Bonderman did.

\n
\n\n\n\n\t" - }, - { - "title" : "Narrative Maps for ‘Choose Your Own Adventure’ Books", - "date_published" : "2017-06-22T17:50:49Z", - "date_modified" : "2017-06-22T17:59:41Z", - "id" : "https://daringfireball.net/linked/2017/06/22/choose-your-own-adventure-maps", - "url" : "https://daringfireball.net/linked/2017/06/22/choose-your-own-adventure-maps", - "external_url" : "http://www.atlasobscura.com/articles/cyoa-choose-your-own-adventure-maps#coincidence", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Sarah Laskow, writing for Atlas Obscura:

\n\n
\n

The last installment of the original “Choose Your Own Adventure”\nseries came out in 1998, but since 2004, Chooseco, founded by\none of the series’ original authors, R.A. Montgomery, has been\nrepublishing classic volumes, as well as new riffs on the form of\ninteractive fiction that seemed ubiquitous in the 1980s and ’90s.\nThe new editions also carry an additional feature — maps of the\nhidden structure of each book.

\n
\n\n

Just looking at the maps included in the article, it’s interesting how wildly varying in complexity these stories were. See also: Christian Swinehart’s color-coded graphical representations of these books.

\n\n

(Via Kottke.)

\n\n\n\n\t" - }, - { - "title" : "John Markoff’s Interview With Scott Forstall and Members of the Original iPhone Team", - "date_published" : "2017-06-22T17:45:20Z", - "date_modified" : "2017-06-22T20:17:34Z", - "id" : "https://daringfireball.net/linked/2017/06/22/markoff-forstall", - "url" : "https://daringfireball.net/linked/2017/06/22/markoff-forstall", - "external_url" : "https://www.youtube.com/watch?v=5xDRdWFdsoQ", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

The Computer History Museum (now on YouTube):

\n\n
\n

Museum Historian John Markoff moderates a discussion with former\niPhone team members Hugo Fiennes, Nitin Ganatra and Scott Herz,\nfollowed by a conversation with Scott Forstall.

\n
\n\n

Fascinating stories.

\n\n

Forstall was great. It’s hard to believe he’s been out of Apple and out of the limelight for 5 years — watching him on stage with Markoff it feels like he never left.

\n\n\n\n\t" - }, - { - "title" : "‘Four Things in Those Two Sentences’", - "date_published" : "2017-06-21T16:55:23Z", - "date_modified" : "2017-06-21T16:55:26Z", - "id" : "https://daringfireball.net/linked/2017/06/21/swisher-kalanick", - "url" : "https://daringfireball.net/linked/2017/06/21/swisher-kalanick", - "external_url" : "https://www.recode.net/2017/6/21/15844848/uber-ceo-travis-kalanick-resigned-board-pressure", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Kara Swisher on Travis Kalanick:

\n\n
\n

Uber confirmed the resignation, and the company’s board issued a\nstatement that said, in part: “Travis has always put Uber first.\nThis is a bold decision and a sign of his devotion and love for\nUber.” (For those who don’t speak fluent tech director, there are\nfour things in those two sentences that are not true.)

\n
\n\n\n\n\t" - }, - { - "title" : "Uber Founder Travis Kalanick Resigns as C.E.O.", - "date_published" : "2017-06-21T16:53:04Z", - "date_modified" : "2017-06-21T16:53:06Z", - "id" : "https://daringfireball.net/linked/2017/06/21/kalanick-out", - "url" : "https://daringfireball.net/linked/2017/06/21/kalanick-out", - "external_url" : "https://www.nytimes.com/2017/06/21/technology/uber-ceo-travis-kalanick.html?_r=0", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Mike Isaac, reporting for The New York Times:

\n\n
\n

In the letter, titled “Moving Uber Forward” and obtained by The\nNew York Times, the investors wrote to Mr. Kalanick that he must\nimmediately leave and that the company needed a change in\nleadership. Mr. Kalanick, 40, consulted with at least one Uber\nboard member, and after long discussions with some of the\ninvestors, he agreed to step down. He will remain on Uber’s board\nof directors.

\n\n

“I love Uber more than anything in the world and at this difficult\nmoment in my personal life I have accepted the investors request\nto step aside so that Uber can go back to building rather than be\ndistracted with another fight,” Mr. Kalanick said in a statement.

\n
\n\n

From the outside, it seems like this was inevitable. It was only a question of when.

\n\n\n\n\t" - }, - { - "title" : "Chris Lattner Out at Tesla", - "date_published" : "2017-06-21T01:52:14Z", - "date_modified" : "2017-06-21T01:54:56Z", - "id" : "https://daringfireball.net/linked/2017/06/20/lattner-tesla", - "url" : "https://daringfireball.net/linked/2017/06/20/lattner-tesla", - "external_url" : "https://mobile.twitter.com/clattner_llvm/status/877341760812232704", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Chris Lattner:

\n\n
\n

Turns out that Tesla isn’t a good fit for me after all. I’m interested to hear about interesting roles for a seasoned engineering leader!

\n
\n\n

That was quick — he was only hired 5 months ago.

\n\n\n\n\t" - }, - { - "title" : "[Sponsor] MNML: A macOS Writing App for Medium.com", - "date_published" : "2017-06-20T23:12:26-04:00", - "date_modified" : "2017-06-24T20:23:18-04:00", - "id" : "https://daringfireball.net/feeds/sponsors/2017/06/mnml_a_macos_writing_app_for_m", - "url" : "https://daringfireball.net/feeds/sponsors/2017/06/mnml_a_macos_writing_app_for_m", - "external_url" : "http://mnml.af/", - "author" : { - "name" : "Daring Fireball Department of Commerce" - }, - "content_html" : "\n

Created for discerning Medium.com writers and publishers, it’s the first dedicated desktop publishing client on the Mac App Store. Featured Worldwide on release, it’s the last writing, blogging, and note-taking app you’ll need.

\n\n

Functional and fun yet mnml af. 🤔 😆 🔥

\n\n\n\n\t" - }, - { - "title" : "The Outline: ‘Inside Apple’s Global War on Leakers’", - "date_published" : "2017-06-20T18:22:34Z", - "date_modified" : "2017-06-20T18:22:37Z", - "id" : "https://daringfireball.net/linked/2017/06/20/apples-war-on-leaks", - "url" : "https://daringfireball.net/linked/2017/06/20/apples-war-on-leaks", - "external_url" : "https://theoutline.com/post/1766/leaked-recording-inside-apple-s-global-war-on-leakers", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

William Turton has quite a scoop for The Outline:

\n\n
\n

A recording of an internal briefing at Apple earlier this month\nobtained by The Outline sheds new light on how far the most\nvaluable company in the world will go to prevent leaks about new\nproducts. […]

\n\n

The briefing, which offers a revealing window into the company’s\nobsession with secrecy, was the first of many Apple is planning to\nhost for employees. In it, Rice and Freedman speak candidly about\nApple’s efforts to prevent leaks, discuss how previous leakers got\ncaught, and take questions from the approximately 100 attendees.

\n
\n\n

There’s some irony in a leaked recording of an internal briefing on stopping leaks.

\n\n

This is news to me:

\n\n
\n

However, Rice says, Apple has cracked down on leaks from its\nfactories so successfully that more breaches are now happening on\nApple’s campuses in California than its factories abroad. “Last\nyear was the first year that Apple [campuses] leaked more than the\nsupply chain,” Rice tells the room. “More stuff came out of Apple\n[campuses] last year than all of our supply chain combined.” […]

\n\n

In the years since Tim Cook pledged to double down on secrecy,\nRice’s team has gotten better at safeguarding enclosures. “In 2014\nwe had 387 enclosures stolen,” he says. “In 2015 we had 57\nenclosures stolen, 50 of which were stolen on the night of\nannounce, which was so painful.” In 2016, Rice says the company\nproduced 65 million housings, and only four were stolen. “So it’s\nabout a one in 16 million loss ratio, which is unheard of in the\nindustry.”

\n
\n\n

There’s a short (15 minute) podcast that accompanies the report, with Turton and The Outline’s Adrianne Jeffries. It’s worth a listen. (It doesn’t seem possible to link directly to a single episode of their podcast, so here’s a direct link for Overcast users.)

\n\n\n\n\t" - }, - { - "title" : "The Talk Show: ‘Egg Freckles’", - "date_published" : "2017-06-20T02:02:46Z", - "date_modified" : "2017-06-20T02:02:48Z", - "id" : "https://daringfireball.net/linked/2017/06/19/the-talk-show-194", - "url" : "https://daringfireball.net/linked/2017/06/19/the-talk-show-194", - "external_url" : "https://daringfireball.net/thetalkshow/2017/06/19/ep-194", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

New episode of my podcast, The Talk Show, with special guest Serenity Caldwell. We look back at WWDC 2017 — iOS 11, the new iPad Pro models, MacOS 10.13 “High Sierra”, updated Mac hardware and a tease at the upcoming iMac Pro, where Apple might go with VR and AR, San Jose as the venue for the event itself, and more.

\n\n

Sponsored by:

\n\n
    \n
  • Warby Parker: Boutique-quality, vintage-inspired glasses at a revolutionary price. Try up to five pairs at home for free.
  • \n
  • Squarespace: Make your next move. Make your next website. Use code gruber for 10% off your first order.
  • \n
  • Fracture: Your photos, printed beautifully directly on glass.
  • \n
\n\n\n\n\t" - }, - { - "title" : "★ Medium and the Scourge of Persistent Sharing Dickbars", - "date_published" : "2017-06-20T00:22:59Z", - "date_modified" : "2017-06-20T20:08:46Z", - "id" : "https://daringfireball.net/2017/06/medium_dickbars", - "url" : "https://daringfireball.net/2017/06/medium_dickbars", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Medium seems to continue to grow in popularity as a publishing platform, and as it does, I’m growing more and more frustrated by their on-screen “engagement” turds. Every Medium site displays an on-screen “sharing” bar that covers the actual content I want to read. This is particularly annoying on the phone, where screen real estate is most precious. Now on iOS they’ve added an “Open in App” button that literally makes the last 1-2 lines of content on screen unreadable. To me these things are as distracting as having someone wave their hand in front of my face while I try to read.

\n\n

Here’s an annotated screenshot (and threaded rant) I posted to Twitter while trying to read Steven Sinofksy’s WWDC 2017 trip report on my iPad Pro review unit last week.

\n\n

Safari already has a built-in Sharing button. It has all the options for sharing I need. And as I scroll the page, it disappears so that I can see as much text on screen as possible. Safari is designed to be reader-friendly, as it should be. But it’s trivial to get that Sharing button back when I want it – just tap the bottom of the screen and there it is. Easy.

\n\n

This is now a very common design pattern for mobile web layouts. Medium is far from alone. It’s getting hard to find a news site that doesn’t put a persistent sharing dickbar down there.

\n\n

More examples:

\n\n\n\n

TechCrunch’s waste of space deserves special mention, for having a persistent navbar at the top and a persistent ad, in addition to their sharing dickbar.

\n\n

I’m sure “engagement” does register higher with these sharing dickbars, but I suspect a big part of that is because of accidental taps. And even so, what is more important, readability or “engagement”? Medium wants to be about readability but that’s hard to square with this dickbar, and especially hard to square with the “Open in App” button floating above it.

\n\n

iOS also has a standard way to prompt users to install the app version of a website — Smart App Banners. And it’s user-dismissible.

\n\n

For any piece over a page long, I read Medium pieces with Safari’s Reader Mode. Medium is supposed to be a reader-optimized layout by default. It should be one of the sites where you’re never even tempted to switch to Reader Mode.

\n\n

I’m frustrated by this design pattern everywhere I see it. But I’m especially disappointed by Medium’s adoption of it. I don’t expect better from most websites. I do expect better from Medium.

\n\n

A website should not fight the browser. Let the browser provide the chrome, and simply provide the content. Web developers know this is right — these dickbars are being rammed down their throats by SEO experts. The SEO folks are the same dopes who came up with the genius strategy of requiring 5-10 megabytes of privacy-intrusive CPU-intensive JavaScript on every page load that slows down websites. Now they come to their teams and say, “Our pages are too slow — we gotta move to AMP so our pages load fast.”

\n\n

I don’t expect to break through to the SEO shitheads running the asylums at most of these publications, but Medium is supposed to be good. When people click a URL and see that it’s a Medium site, their reaction should be “Oh, good, a Medium site — this will be nice to read.” Right now it’s gotten to the point where when people realize an article is on Medium, they think, “Oh, crap, it’s on Medium.”

\n\n\n\n " - }, - { - "title" : "Microsoft Surface Laptop Teardown", - "date_published" : "2017-06-19T19:43:57Z", - "date_modified" : "2017-06-19T20:07:54Z", - "id" : "https://daringfireball.net/linked/2017/06/19/surface-laptop-ifixit", - "url" : "https://daringfireball.net/linked/2017/06/19/surface-laptop-ifixit", - "external_url" : "https://www.ifixit.com/Teardown/Microsoft+Surface+Laptop+Teardown/92915", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

iFixit gave the Surface Laptop a 0 out of 10 on their “Repairability Score”. The lowest anything from Apple has ever gotten is a 1, I believe.

\n\n
\n

Verdict: The Surface Laptop is not a laptop. It’s a glue-filled\nmonstrosity. There is nothing about it that is upgradable or\nlong-lasting, and it literally can’t be opened without destroying\nit. (Show us the procedure, Microsoft, we’d love to be wrong.)

\n
\n\n

iFixit’s point of view on this is logical, and they’re certainly not alone in wishing for the good old days of user-accessible and user-upgradeable components. But it’s silly to argue that the Surface Laptop is “not a laptop” only because it’s a sealed box. It’s like saying the iPhone is not a phone because it doesn’t have a replaceable battery.

\n\n

Update: Apple’s AirPods got a 0/10 from iFixit. That just goes to show how little correlation there is between iFixit’s concept of repairability and whether a product is good or not. I consider AirPods to be Apple’s best new product in years.

\n\n\n\n\t" - }, - { - "title" : "Standard Ebooks", - "date_published" : "2017-06-19T19:40:57Z", - "date_modified" : "2017-06-20T00:37:38Z", - "id" : "https://daringfireball.net/linked/2017/06/19/standard-ebooks", - "url" : "https://daringfireball.net/linked/2017/06/19/standard-ebooks", - "external_url" : "https://standardebooks.org/", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Standard Ebooks:

\n\n
\n

Standard Ebooks is a volunteer driven, not-for-profit project\nthat produces lovingly formatted, open source, and free public\ndomain ebooks.

\n\n

Ebook projects like Project Gutenberg transcribe ebooks and\nmake them available for the widest number of reading devices.\nStandard Ebooks takes ebooks from sources like Project\nGutenberg, formats and typesets them using a carefully designed\nand professional-grade style guide, lightly modernizes them,\nfully proofreads and corrects them, and then builds them to\ntake advantage of state-of-the-art ereader and browser\ntechnology. […]

\n\n

Other free ebooks don’t put much effort into professional-quality\ntypography: they use "straight" quotes instead of “curly” quotes,\nthey ignore details like em- and en-dashes, and they look more\nlike early-90’s web pages instead of actual books.

\n\n

The Standard Ebooks project applies a rigorous and modern\ntypography manual when developing each and every ebook to ensure\nthey meet a professional-grade and consistent typographical\nstandard. Our ebooks look good.

\n
\n\n

What a fantastic project. Project Gutenberg is an amazing library, but their books are a mess typographically. (Via Daniel Bogan.)

\n\n\n\n\t" - }, - { - "title" : "The Size of iPhone’s Top Apps Has Increased by 1,000 Percent in Four Years", - "date_published" : "2017-06-19T19:23:51Z", - "date_modified" : "2017-06-19T19:23:52Z", - "id" : "https://daringfireball.net/linked/2017/06/19/sensor-tower-ios-app-bloat", - "url" : "https://daringfireball.net/linked/2017/06/19/sensor-tower-ios-app-bloat", - "external_url" : "https://sensortower.com/blog/ios-app-size-growth", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Randy Nelson, writing for the Sensor Tower blog:

\n\n
\n

According to Sensor Tower’s analysis of App Intelligence,\nthe total space required by the top 10 most installed U.S. iPhone\napps has grown from 164 MB in May 2013 to about 1.8 GB last month,\nan 11× or approximately 1,000 percent increase in just four years.\nIn the following report, we delve deeper into which apps have\ngrown the most.

\n
\n\n

Apple really needs to do something about this. It’s not just that these apps are too big, but some of them issue software updates every week (or even more frequently). It’s a huge waste of bandwidth, time, and on-device storage space.

\n\n\n\n\t" - }, - { - "title" : "Microsoft AI Team Achieves Perfect Score on Atari 2600 Ms. Pac-Man", - "date_published" : "2017-06-19T19:14:49Z", - "date_modified" : "2017-06-19T19:17:41Z", - "id" : "https://daringfireball.net/linked/2017/06/19/ms-pac-man-ai", - "url" : "https://daringfireball.net/linked/2017/06/19/ms-pac-man-ai", - "external_url" : "https://www.theverge.com/2017/6/14/15801700/microsoft-ai-perfect-ms-pac-man-score", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Dani Deahl writing for The Verge:

\n\n
\n

At long last, the perfect score for arcade classic Ms. Pac-Man has\nbeen achieved, though not by a human. Maluuba — a deep learning\nteam acquired by Microsoft in January — has created an AI system\nthat’s learned how to reach the game’s maximum point value of\n999,900 on Atari 2600, using a unique combination of reinforcement\nlearning with a divide-and-conquer method.

\n
\n\n

Unlike the notoriously bad 2600 port of Pac-Man, the Ms. Pac-Man port was both fun and true to the spirit of the coin-op.

\n\n\n\n\t" - }, - { - "title" : "Why Reach Navigation Should Replace the Navbar in iOS Design", - "date_published" : "2017-06-19T18:52:46Z", - "date_modified" : "2017-06-19T18:57:25Z", - "id" : "https://daringfireball.net/linked/2017/06/19/ellis-reach-navigation", - "url" : "https://daringfireball.net/linked/2017/06/19/ellis-reach-navigation", - "external_url" : "https://medium.com/tall-west/lets-ditch-the-nav-bar-3692cb17cc67", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Brad Ellis:

\n\n
\n

As devices change, our visual language changes with them. It’s\ntime to move away from the navbar in favor of navigation within\nthumb-reach. For the purposes of this article, we’ll call that\nReach Navigation.

\n
\n\n

This design trend is clearly already underway, and Ellis does a terrific job explaining why it’s a good idea.

\n\n

I can think of a few factors that led to the original iPhone having a top-of-the-screen UI for navigation. First, at just 3.5 inches diagonally, the whole screen was reachable. But another factor might be as simple as the fact that “navigation” was always at the top on desktops — window titles and controls have always been at the top on Mac and Windows. The iPhone didn’t use windows, per se, but there was a certain familiarity with having the titles and controls like Back/Close/Done buttons at the top. Something like the iOS 10 bottom-heavy design of Apple Maps is wholly different from a desktop UI design — as it should be.

\n\n\n\n\t" - }, - { - "title" : "Ben Thompson on Amazon and Whole Foods", - "date_published" : "2017-06-19T18:25:15Z", - "date_modified" : "2017-06-19T18:25:16Z", - "id" : "https://daringfireball.net/linked/2017/06/19/thompson-amazon-whole-foods", - "url" : "https://daringfireball.net/linked/2017/06/19/thompson-amazon-whole-foods", - "external_url" : "https://stratechery.com/2017/amazons-new-customer/", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Great piece by Ben Thompson on Amazon’s intended acquisition of Whole Foods:

\n\n
\n

As Mackey surely understood, this meant that AmazonFresh was at a\ncost disadvantage to physical grocers as well: in order to be\ncompetitive AmazonFresh needed to stock a lot of perishable items;\nhowever, as long as AmazonFresh was not operating at meaningful\nscale a huge number of those perishable items would spoil. And,\ngiven the inherent local nature of groceries, scale needed to be\nachieved not on a national basis but a city one.

\n\n

Groceries are a fundamentally different problem that need a\nfundamentally different solution; what is so brilliant about this\ndeal, though, is that it solves the problem in a fundamentally\nAmazonian way.

\n
\n\n\n\n\t" - }, - { - "title" : "Ikea Details Plans for Furniture Placement App Powered by Apple’s ARKit", - "date_published" : "2017-06-19T17:59:44Z", - "date_modified" : "2017-06-19T17:59:46Z", - "id" : "https://daringfireball.net/linked/2017/06/19/ikea-arkit", - "url" : "https://daringfireball.net/linked/2017/06/19/ikea-arkit", - "external_url" : "https://www.macrumors.com/2017/06/19/ikea-plans-furniture-app-arkit/", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Mitchel Broussard:

\n\n
\n

At WWDC this year, Apple senior vice president of software\nengineering Craig Federighi performed a demo of the company’s new\naugmented reality platform, ARKit, while mentioning popular\nfurniture company IKEA as an upcoming partner in the technology.\nSimilarly, Apple CEO Tim Cook referenced an Ikea AR partnership in\na recent interview with Bloomberg Businessweek.

\n\n

Now, Ikea executive Michael Valdsgaard has spoken about the\ncompany’s partnership with Apple and ARKit, describing an all-new\naugmented reality app that will help customers make “reliable\nbuying decisions” for Ikea’s big ticket items.

\n
\n\n

Very cool idea — probably the sort of thing that’s going to be common soon. I’m curious how much of a leg up ARKit will give iOS on this front.

\n\n\n\n\t" - }, - { - "title" : "Squarespace Domains", - "date_published" : "2017-06-18T18:57:06Z", - "date_modified" : "2017-06-19T03:26:42Z", - "id" : "https://daringfireball.net/linked/2017/06/18/squarespace-domains", - "url" : "https://daringfireball.net/linked/2017/06/18/squarespace-domains", - "external_url" : "http://bit.ly/2kx5Ax5", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

My thanks to Squarespace for sponsoring last week’s DF RSS feed. Squarespace handles everything related to creating, hosting, and maintaining a website, including domain name registration.

\n\n

Buying a domain from Squarespace is quick, simple, and fun. Search for the domain you want, or type any word or phrase into the search field and Squarespace will suggest some great options. Every domain comes with a beautiful, ad-free parking page, WHOIS Privacy, and a 2048-bit SSL certificate to secure your website — all at no additional cost. Once you lock down your domain, create a beautiful website with one of Squarespace’s award-winning templates. Try Squarespace for free. When you’re ready to subscribe, get 10% off at squarespace.com with offer code “DARING17”.

\n\n\n\n\t" - }, - { - "title" : "Designing the Worst Volume Sliders Possible", - "date_published" : "2017-06-18T18:54:31Z", - "date_modified" : "2017-06-18T18:54:32Z", - "id" : "https://daringfireball.net/linked/2017/06/18/worst-volume-sliders", - "url" : "https://daringfireball.net/linked/2017/06/18/worst-volume-sliders", - "external_url" : "https://www.designernews.co/stories/84443-redditors-design-worst-volume-sliders-possible", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

This is a fun challenge.

\n\n\n\n\t" - }, - { - "title" : "John Markoff to Interview Scott Forstall Next Week", - "date_published" : "2017-06-15T19:34:33Z", - "date_modified" : "2017-06-15T20:03:29Z", - "id" : "https://daringfireball.net/linked/2017/06/15/markoff-forstall", - "url" : "https://daringfireball.net/linked/2017/06/15/markoff-forstall", - "external_url" : "http://www.computerhistory.org/events/upcoming/#putting-your-finger-it-creating", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Blockbuster event next week at the Computer History Museum in Mountain View:

\n\n
\n

How did iPhone come to be? On June 20, four members of the\noriginal development team will discuss the secret Apple project,\nwhich in the past decade has remade the computer industry, changed\nthe business landscape, and become a tool in the hands of more\nthan a billion people around the world.

\n\n

Part 1: Original iPhone Engineers Nitin Ganatra, Scott Herz, and\nHugo Fiennes in Conversation with John Markoff

\n\n

Part 2: Original iPhone Software Team Leader Scott Forstall in\nConversation with John Markoff

\n
\n\n

It kills me that I can’t make this. Hopefully there will be video.

\n\n

Here’s the thing: Forstall was obviously a divisive figure inside Apple. He saw himself as an indispensable man after Steve Jobs died, and it turns out he wasn’t.

\n\n

But there can be no dispute that Forstall led one of the most successful software projects ever undertaken. It’s a cliche to say that they achieved the impossible, but what Forstall’s team achieved was considered by many — including many of the members of the team — impossible. But they did it, and in the ensuing years they kept making iOS better and better. It’s not just that they managed to ship the original iPhone OS in June 2007, but the entire run up through Forstall’s ouster from the company was simply amazing.

\n\n

Across the company, it’s clear that Forstall’s style was not popular. But I know many people who worked on his iOS team, and most of them loved working for him, or at the very least appreciated working for him. The thing I’ve heard over and over is that Forstall was incredibly demanding, yes, but if you were on his team and did good work he had your back.

\n\n

Forstall pretty much hasn’t said a damn thing about Apple since he left the company five years ago. So if he opens up at all to Markoff, this could be fascinating. His team’s story about actually implementing the original iPhone remains largely untold.

\n\n\n\n\t" - }, - { - "title" : "Brian Merchant Has Tony Fadell on Tape", - "date_published" : "2017-06-15T19:16:54Z", - "date_modified" : "2017-06-15T19:16:55Z", - "id" : "https://daringfireball.net/linked/2017/06/15/merchant-fadell-on-tape", - "url" : "https://daringfireball.net/linked/2017/06/15/merchant-fadell-on-tape", - "external_url" : "https://www.theverge.com/2017/6/15/15804782/vergecast-brian-merchant-author-the-one-device-special-edition", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Nilay Patel, announcing a special episode of The Vergecast with The One Device author Brian Merchant:

\n\n
\n

And, of course, we talk about the quotes from Tony Fadell and\nBrett Bilbrey in the excerpt we just published, in which Fadell\ntells a story about Phil Schiller arguing the iPhone should have a\nhardware keyboard. Schiller has said the story isn’t true, and\nFadell has tried to walk it back as well.

\n\n

“So I wasn’t in the room at Apple 10, 15 years ago when this would\nhave happened,” says Merchant, who has the exchange on tape. “But\nthis is a quote verbatim as Tony Fadell who was in the room told\nit to me. He told me this quote in such detail and he gave such a\nvivid account, and I had no reason to believe it was untrue.”

\n\n

Merchant says the controversy has “blown him away.”

\n
\n\n

I figured Merchant had Fadell’s interview recorded. The quotes were too extensive not to have been recorded. It’s pretty clear what happened: Fadell told Merchant exactly what he’s quoted as saying, but now that he’s seen how it’s playing out, he wants to walk it back. It’s a little late for that.

\n\n\n\n\t" - }, - { - "title" : "★ iPad Pro Review Roundup", - "date_published" : "2017-06-14T03:59:00Z", - "date_modified" : "2017-06-14T21:22:44Z", - "id" : "https://daringfireball.net/2017/06/ipad_pro_review_roundup", - "url" : "https://daringfireball.net/2017/06/ipad_pro_review_roundup", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Federico Viticci, MacStories, “The 10.5-inch iPad Pro: Future-Proof”:

\n\n
\n

A good way to think about the iPad’s new display with ProMotion\nis not the difference between low-res and Retina screens, but\nthe jump from 30fps to 60fps. You see more of every animation.\nText is more legible when you scroll and doesn’t judder. It’s\nhard to explain and it has to be seen and experienced to be\nfully understood. Every scroll, page transition, and app launch\nanimation on the 10.5″ iPad Pro is absurdly smooth to the point\nof feeling unrealistic at first — hence the common reaction\nthat something doesn’t quite compute. But as you spend some time\nwith the new iPad and start using it on a daily basis, its\ndisplay becomes normal and you wish that other Apple displays\nwere the same.

\n\n

I’m not even a week into my tests with the 10.5″ iPad Pro, and\nI think scrolling on my first-gen 12.9″ iPad Pro looks choppy\nnow. I’d be surprised if 120Hz displays with ProMotion don’t\nexpand to the iPhone later this year and other Apple computers\nin the future. The combination of hardware and software really\nis that good.

\n
\n\n

Last year when True Tone was introduced with the 9.7-inch iPad Pro, Phil Schiller said something to the effect of “Once you get used to True Tone, you can’t go back.” I optimistically took that as a sign that the iPhone 7 would have True Tone. It did not, and the reason is probably that True Tone requires additional hardware sensors on the front face to pick up the ambient light temperature, and the iPhone has less room for additional sensors. But with ProMotion, I’m really hopeful that it’ll make its way into this year’s new iPhones. ProMotion doesn’t require additional sensors — only a super-fast GPU (which the iPhone will have) and intricate software support in iOS (which work Apple has already done for the iPad Pro).

\n\n

Anyway, it’s really hard to quote just one bit from Viticci’s review. If you only thoroughly read one review of the new iPad Pro, it should be his. Nobody outside Apple cares as much about iPad as he does.

\n\n
\n\n

Matthew Panzarino, TechCrunch, “Apple Pays Off Its Future-of-Computing Promise With iPad Pro”:

\n\n
\n

After playing with the new iPad Pro 10.5” for a few days, I am\nconvinced that it’s fairly impossible to do a detailed review of\nit in its current state.

\n\n

Not because there is some sort of flaw, but because it was clearly\ndesigned top to bottom as an empty vessel in which to pour iOS 11.

\n\n

Every feature, every hardware advancement, every piece of\nunderstated technical acrobatics is in the service of making\nApple’s next-generation software shine.

\n
\n\n
\n\n

Dieter Bohn, The Verge, “iPad Pro 10.5 Review: Overkill”:

\n\n
\n

I was all set to complain that increasing the size from 9.7 to\n10.5 was not a big enough jump to justify requiring people to buy\nnew keyboards and accessories. Then I started typing on the\non-screen keyboard and on the new hardware Smart Keyboard. Even\nthough I’m dubious about Apple’s claim that the software keyboard\nis “full size”, I find the slight size increase makes touch typing\nmuch easier. It’s still a little cramped, but it’s much easier to\nbounce between this and a real keyboard now.

\n
\n\n

It really does make a difference in typing, and no practical difference at all in terms of holdability.

\n\n

Bohn again:

\n\n
\n

To me, if you’re going to spend $650 on a computer, it should\nalmost surely be your main computer. And if you’re going to make\nthe iPad Pro your main computer, you should probably get more than\n64GB of storage and you should also probably get a keyboard to go\nwith it (to say nothing of the Apple Pencil). It hits the $1,000\nmark very quickly.

\n
\n\n

I don’t agree with the notion that a $650 computer should be your “main computer” at all. Apple stuff isn’t for the budget-conscious — news at 11.

\n\n
\n\n

Brian X. Chen, The New York Times, “New iPad Pro Inches Toward Replacing PC, but Falls Short”:

\n\n
\n

Five years later, Mr. Jobs’s successor, Timothy D. Cook, took the\niPad a step further. Unveiling the iPad Pro, a souped-up tablet\nthat worked with Apple’s keyboard and stylus, he remarked that\npeople would try the product and “conclude they no longer need to\nuse anything else, other than their phones.”

\n\n

That prediction has not appeared to come true. Many professionals\nsay they use an iPad in addition to a personal computer, and sales\nof iPads have shrunk quarter after quarter for more than a year,\nan indication that hordes of people were not trading in their PCs\nfor tablets just yet.

\n\n

That situation is unlikely to change with Apple’s newest iPad Pro,\nwhich will be released this week. […] But after about a week of\ntesting the 10.5-inch iPad Pro, I concluded that Apple’s\nprofessional tablet still suffers from some of the same problems\nwhen compared with a laptop.

\n
\n\n

That’s a slanted truncation of Cook’s quote. Cook’s full quote: “Yes, the iPad Pro is a replacement for a notebook or a desktop for many, many people. They will start using it and conclude they no longer need to use anything else, other than their phones.” Chen’s truncation makes it sound like Cook claimed the iPad Pro was a Mac or Windows replacement for everyone. He didn’t. And the fact that the new iPad Pro debuted alongside new MacBooks, MacBook Pros, and even more-megahertz-in-the-box MacBook Airs shows that Apple doesn’t think so either. Update: And I completely forgot to mention the solid updates to the iMacs and the announcement of the iMac Pro.

\n\n

I prefer a laptop to an iPad Pro” is very different from “A laptop is better than an iPad Pro”. Me, personally, I much prefer working on a MacBook Pro to an iPad Pro. But I can see why others feel the opposite. That’s the whole point of Apple’s strategy of keeping them separate, rather than unifying them Microsoft Surface-style.

\n\n

iPad’s slowly diminishing sales are a real thing. But I don’t think that can be used as a gauge for whether more and more people are using an iPad as their main computer. And iPad sales are still more than double those of the Mac. There’s no reason to doubt that “many, many people” are concluding they no longer need a Mac or PC.

\n\n
\n\n

Andrew Cunningham, Ars Technica, “The 10.5-Inch iPad Pro Is Much More “Pro” Than What It Replaces”:

\n\n
\n

Of all the computers Apple sells, none of them has screens that do\nquite as much stuff as the iPad Pros are doing.

\n\n

That list starts with DCI-P3 color gamut support (new in the\n12.9-inch Pro, returning to the smaller one) and an\nanti-reflective coating, features also present in recent iMacs and\nMacBook Pros. But the True Tone feature, which detects the color\ntemperature of the ambient light, adjusts the display’s color\ntemperature to match. Most significantly, the iPad’s refresh rate\nhas been bumped up to 120Hz, twice the normal 60Hz. The screens in\nthe iPad Pros are the best screens Apple ships, which is\nappropriate for a thing that’s just a giant screen by design.

\n\n

The 10.5-inch Pro has a 2224×1668 screen, up just a little bit\nfrom the 2048×1536 in 9.7-inch iPads. The density is identical, so\nphotos and text are exactly the same size they were before; you\ncan just fit a bit more of them on-screen at once.

\n
\n\n

That’s important to note. There was some clever speculation by Dan Provost a few months ago that the 10.5-inch iPad would have the same pixel dimensions as the 12.9-inch iPad Pro, with a higher pixels-per-inch density. That’s what Apple did with the iPad Mini. The problem with that speculation is that while the math worked out, the size of things on screen would not. Everything would be shrunk by 20 percent. Not everyone’s eyes can handle that. That’s fine for the Mini — which is often used by sharp-eyed children — but not fine for the standard size iPad.

\n\n

I had been thinking that maybe what Apple would do is what Provost suggested, but offer a choice between standard and zoomed mode like the Plus-sized iPhones do. Nope. I think what they’ve done is better though, because I think a scaled “zoomed” interface would look blurry at just 324 ppi. The iPhone Plus displays have a resolution of 401 ppi.

\n\n
\n\n

Harry McCracken, Fast Company, “A Better Window Into The World Of Apps”:

\n\n
\n

You can suss out a lot about Apple’s priorities from the aspects\nof a product it leaves alone and the ones it never stops\nobsessing over.

\n\n

Consider the iPad. Every generation of Apple’s tablet since the\nfirst one in 2010 has had the same stated battery life–“up to 10\nhours”–which suggests that the company thinks that shooting for\nanything in excess of that would be wasted effort.

\n\n

That 2010 iPad weighed a pound and a half, and felt a bit hefty in\nthe hand. With 2013’s iPad Air, Apple whittled that down to about\na pound. And there the mid-sized iPads have stayed,\nweight-reduction mission accomplished.

\n\n

However, when it comes to the iPad’s display, Apple has never been\nsatisfied to rest on its technological laurels.

\n
\n\n\n\n " - }, - { - "title" : "★ The Knives Come Out for Phil Schiller in Brian Merchant’s ‘The One Device: The Secret History of the iPhone’", - "date_published" : "2017-06-13T20:46:25Z", - "date_modified" : "2017-06-13T23:15:31Z", - "id" : "https://daringfireball.net/2017/06/the_knives_come_out_for_schiller", - "url" : "https://daringfireball.net/2017/06/the_knives_come_out_for_schiller", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

The Verge has an exclusive (and lengthy) excerpt from Brian Merchant’s The One Device: The Secret History of the iPhone, which comes out next week. Merchant seemingly has many first-hand sources on the record, including Tony Fadell and perhaps Scott Forstall. (I say “perhaps” because it’s not clear from the excerpt whether Forstall spoke to Merchant, or if Merchant got the Forstall quotes from somewhere else. It seems like there should be a lot more from Forstall in this story if he actually talked to Merchant.)

\n\n

But Fadell spoke to Merchant extensively, including this shot at Phil Schiller:

\n\n
\n

The iPod phone was losing support. The executives debated which\nproject to pursue, but Phil Schiller, Apple’s head of marketing,\nhad an answer: Neither. He wanted a keyboard with hard buttons.\nThe BlackBerry was arguably the first hit smartphone. It had an\nemail client and a tiny hard keyboard. After everyone else,\nincluding Fadell, started to agree that multitouch was the way\nforward, Schiller became the lone holdout.

\n\n

He “just sat there with his sword out every time, going, ‘No,\nwe’ve got to have a hard keyboard. No. Hard keyboard.’ And he\nwouldn’t listen to reason as all of us were like, ‘No, this works\nnow, Phil.’ And he’d say, ‘You gotta have a hard keyboard!’”\nFadell says.

\n
\n\n

I don’t know if it’s true or not that Schiller was singlehandedly pushing for a Blackberry-style keyboard. But even if true, it only looks foolish in hindsight, especially if this argument took place before the iPhone’s software team had come up with a proof-of-concept software keyboard. Today it’s clear that the iPhone needed a good keyboard, and that a touchscreen keyboard can be a good keyboard. Neither of those things was obvious in 2005. And in the context of this story, it’s clear that at the time of this purported argument, Steve Jobs and Apple weren’t yet sure if the iPhone should be a pocket-sized personal computer or a consumer electronics product that would have no more need for a keyboard (hardware or software) than an iPod did. My guess is that Schiller was insisting that the iPhone needed to be a personal computer, not a mere gadget, and it wasn’t unreasonable to believe a software keyboard wouldn’t be good enough. For chrissakes there were critics who insisted that the iPhone’s software keyboard wasn’t good enough for years after the iPhone actually shipped.

\n\n

I do know that Schiller’s hard-charging, brusque style and his obvious political acumen have made him a lot of enemies over the years. It sounds like Fadell is one of them.

\n\n

So I’ll just say this: this story about Phil Schiller pushing for a hardware keyboard comes from one source (so far — if anyone out there can back that up, my window is always open for little birdies), and that one source is the guy who admittedly spent over a year working on iPhone prototypes with a click wheel interface.

\n\n

Then there’s this:

\n\n
\n

Schiller didn’t have the same technological acumen as many of the\nother execs. “Phil is not a technology guy,” Brett Bilbrey, the\nformer head of Apple’s Advanced Technology Group, says. “There\nwere days when you had to explain things to him like a\ngrade-school kid.” Jobs liked him, Bilbrey thinks, because he\n“looked at technology like middle America does, like Grandma and\nGrandpa did.”

\n
\n\n

Hats off to Bilbrey for putting his name on this quote, but having spoken to Schiller both on- and off-the-record many times, the idea that he “looks at technology … like Grandma and Grandpa did” and needs things explained to him “like a grade-school kid” is bullshit. Especially off-the-record, Schiller can drill down on technical details to a surprising degree. I don’t know what Schiller did to piss off Bilbrey, but Bilbrey either has a huge chip on his shoulder or was severely misquoted by Merchant.1

\n\n

Anyway, I sure wish this book excerpt had come out before my live episode of The Talk Show last week — now I do have one more question I wish I’d gotten to ask Schiller.

\n\n
\n
\n
    \n
  1. \n

    Here’s a story from Yoni Heisler for Network World on Brett Bilbrey’s retirement from Apple in 2014. Bilbrey headed Apple’s Technology Advancement Group. Merchant describes Bilbrey as having led “Apple’s Advanced Technology Group”. It’s a small detail, and the names are clearly similar, but the Advanced Technology Group was Larry Tesler’s R&D division at Apple, from 1986-1997. It was among the numerous divisions and products that Steve Jobs shitcanned after he rejoined the company. ↩︎

    \n
  2. \n
\n
\n\n\n\n " - }, - { - "title" : "★ The 2017 iPad Pros", - "date_published" : "2017-06-12T22:33:42Z", - "date_modified" : "2017-06-13T23:15:34Z", - "id" : "https://daringfireball.net/2017/06/the_2017_ipad_pros", - "url" : "https://daringfireball.net/2017/06/the_2017_ipad_pros", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

I’ve spent the last week using a new 10.5-inch iPad Pro, and this is, in many ways, the easiest product review I’ve ever written. There are several significant improvements to the hardware, and no tradeoffs or downsides. There is no “but”.

\n\n
    \n
  • Display: The new iPad Pros have the best displays of any computer I’ve ever seen. True Tone plus ProMotion is simply terrific. (The first generation 12.9-inch iPad Pro didn’t have True Tone; with these new models, the only noticeable difference between the 12.9- and 10.5-inch models is the size.) You really do have to see the 120 Hz refresh rate in person — and play with it while scrolling content on screen — to get it. You can actually read text as it’s moving during a scroll. It’s not as significant as the jump from non-retina to retina, but it’s in that ballpark.

  • \n
  • Pencil: The latency of the Apple Pencil on a first-generation iPad Pro is the best I had ever seen for any stylus on any device at any price. The latency of the Apple Pencil on the new iPad Pro is so much better — so much closer to ink-on-paper imperceptibility — that you have to try it to believe it. It’s the one thing that really makes the first-gen iPad Pro feel “slow”.

  • \n
  • Size: The increase in size is perfect. The footprint for a “regular” iPad has, until now, remained unchanged since the original iPad in 2010. That 9.7-inch display size was nearly perfect. This 10.5-inch display size is better though. Apple said during the keynote that typing on the on-screen keyboard is surprisingly better given just a bit more room, and I agree. And typing on the Smart Keyboard cover is way better than on last year’s 9.7-inch iPad Pro. In hand it doesn’t feel bigger at all. It feels like there were no trade-offs whatsoever in increasing the display size and overall device footprint. Part of that is because the weight has remained completely unchanged. I have had zero problems — not one — with the decreased bezel area. Apple’s inadvertent touch detection game is on point.

  • \n
  • Battery: Battery life is great, as expected.

  • \n
  • Performance: Apple’s in-house chip team continues to amaze. No one buys an iPad because of CPU benchmarks, but the new iPad Pro’s CPU performance is mind-boggling. Forget about comparisons to the one-port MacBook — the iPad Pro blows that machine out of the water performance-wise. The astounding thing is that the new iPad Pro holds its own against the MacBook Pro in single-core performance — around 3,900 on the Geekbench 4 benchmark for the iPad Pro vs. around 4,200–4,400 for the various configurations of 13- and 15-inch MacBook Pros.1 Multi-core performance has effectively doubled from the first generation of iPad Pro. That sort of year-over-year increase just doesn’t happen anymore, but here we are. The new iPad Pro gets a multi-core Geekbench 4 score of around 9200; the brand-new Core M3-based MacBook gets a multi-core score of around 6800. Again, this isn’t why people buy iPads — the iPad took off like a rocket in 2010 back when it was way slower (way way way slower) than even the slowest MacBook — but I think it is vastly underappreciated just how significantly Apple’s chip team is pulling ahead of the industry, especially Intel.2

  • \n
\n\n
\n\n

All that said, the real story of these new iPad Pro models can’t be told today, because that story is iOS 11. I think iOS 11’s iPad-focused features are the entire reason why Apple waited until WWDC to unveil them. They could have held an event for them back in April, when they released the new starting-at-just-$329 9.7-inch iPad, but if they did, the only new software they could have demoed was Clips. I love Clips, but it’s just a fun little tool and doesn’t show off anything particular to iPad compared to iPhone.

\n\n

Again, the new iPad Pro hardware is almost too good to be true, but the iPad story Apple unveiled last week is iOS 11.

\n\n

It’s not fair to review a product running a developer beta of the OS — let alone the first (and generally buggiest) beta. So let’s stop the “review” right here: the new iPad Pros running iOS 10.3.2 are the best iPads ever made. You shouldn’t hesitate to buy one today, and if you do get one now, you should wait until iOS 11 ships in the fall to upgrade, or at the very least wait for a non-developer public beta of iOS 11 this summer before upgrading.

\n\n

But if you are reckless enough to install the iOS 11 beta on the new iPad Pro? Holy smokes is this better. I used the iPad Pro for a full week with iOS 10.3.2 because that’s the product that’s shipping, but after upgrading to iOS 11 beta 1 this morning and using it to write this entire review,3 I’m just blown away by how much more useful this machine is, and how much easier it is to work with 5 or 6 apps at a time.

\n\n

I would never recommend running a beta of any OS on any device that’s used for production purposes, so don’t take this as such, but for me personally, I can’t see going back to iOS 10.3.2 on any iPad that can handle it. It feels like a hand has been untied from behind my back, and this amazing hardware has finally been allowed to run free.

\n\n
\n
\n
    \n\n
  1. \n

    You can browse Geekbench’s database of results for Mac and iOS↩︎

    \n
  2. \n\n
  3. \n

    Apple’s A10X chip is so high-performing that I think it’s put Apple in a slightly uncomfortable position marketing-wise. They can’t brag about it fully without making Intel (and by implication, their own MacBooks) look bad, and Intel remains an important partner for Apple. I don’t think it’s a coincidence that the most impressive iPad demo during the keynote (and the one that contained the most bragging about its performance compared to PCs) was done by a third-party developer — Ash Hewson of Serif, demonstrating Affinity Photo — not Greg Joswiak or anyone else from Apple. ↩︎︎

    \n
  4. \n\n
  5. \n

    Full disclosure: I went back to my Mac to write these 3 footnotes. That’s due more to the byzantine way I mark up footnotes than any limitation inherent to iOS vs. MacOS. But it feels worth noting. ↩︎︎

    \n
  6. \n\n
\n
\n\n\n\n " - }, - { - "title" : "★ In-App Purchase Scams in the App Store", - "date_published" : "2017-06-10T20:20:10Z", - "date_modified" : "2017-06-10T20:20:12Z", - "id" : "https://daringfireball.net/2017/06/in-app_purchase_scams_in_the_app_store", - "url" : "https://daringfireball.net/2017/06/in-app_purchase_scams_in_the_app_store", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

Great investigative piece by Johnny Lin looking into a top-10 highest grossing app named “Mobile protection :Clean & Security VPN” (punctuation and grammatical errors sic), from a developer named Ngan Vo Thi Thuy:

\n\n
\n

“Full Virus, Malware scanner”: What? I’m pretty sure it’s\nimpossible for any app to scan my iPhone for viruses or malware,\nsince third party apps are sandboxed to their own data, but let’s\nkeep reading…

\n\n

“You will pay $99.99 for a 7-day subscription”

\n\n

Uhh… come again?

\n
\n\n

There should be no “virus and malware” scanners in the App Store. None. iOS does not need anti-virus software. The App Store sandboxing rules mean that anti-virus software couldn’t really do anything useful anyway. And by allowing them to be listed on the store, it creates the false impression that Apple thinks you might need anti-virus software.

\n\n

But do-nothing anti-virus utilities that are scamming people into $100/week subscriptions? That’s downright criminal.

\n\n

Lin shows that “Mobile protection :Clean & Security VPN” is not alone. The productivity top-grossing list is riddled with similar scam apps.

\n\n

Given how many legitimate developers are still having problems getting their apps approved due to seemingly capricious App Store reviewer decisions, it’s doubly outrageous that these apps have made their way onto the store in the first place. These are the exact sort of apps that the App Store review process should be primarily looking to block.

\n\n

And there is no excuse for Apple not having flagged them after the fact, once they started generating significant revenue. It’s downright mind boggling that this horrendous “Mobile protection :Clean & Security VPN” app made it all the way into the top 10 without getting flagged.

\n\n

Based on Lin’s research, the pattern is simple:

\n\n
    \n
  1. Create a scammy utility app and get it into the store. Make it a free download with an expensive in-app subscription.
  2. \n
  3. Bid on common keywords like “virus”, “VPN”, and “wi-fi”.
  4. \n
  5. Get tens of thousands of downloads thanks to the top-of-results placement from the ad.
  6. \n
  7. Spring the in-app subscription prompt on your users and make money with a response rate of even just a fraction of 1 percent.
  8. \n
\n\n

Apple needs to remove these apps from the App Store, and prevent such apps from getting into the store in the first place. They should reconsider the effects of allowing developers to buy their way to the top spot in search results. And they should police the top-grossing lists for apps that are pulling scams — the most important scams to catch are the successful ones.

\n\n

Lastly, every single dollar these apps have generated should be refunded to the victims of these scams.

\n\n\n\n " - }, - { - "title" : "★ Update on The Talk Show Live From WWDC 2017", - "date_published" : "2017-06-02T05:41:06Z", - "date_modified" : "2017-06-02T06:13:18Z", - "id" : "https://daringfireball.net/2017/06/update_on_the_talk_show_live_from_wwdc_2017", - "url" : "https://daringfireball.net/2017/06/update_on_the_talk_show_live_from_wwdc_2017", - "author" : { - "name" : "John Gruber" - }, - "content_html" : "\n

On Wednesday I put the first 500 tickets on sale for next week’s live show from WWDC. They sold out in 7 minutes.

\n\n

The California Theatre in San Jose has both an orchestra level and a balcony. That first bunch of tickets separated the two. After talking with the staff at the theater today, they recommended making all tickets general admission and allowing their ushers to fill the orchestra level first, and then direct remaining ticket holders to the balcony. So, all tickets, including those sold Wednesday, are now simply general admission. Everyone paid the same price, so I think this is fair, but I do apologize for any confusion. The theater is beautiful, and there are no bad seats.

\n\n

The next batch of tickets will go on sale today, Friday, at 1p ET/10a PT. Given what happened Wednesday, I expect them to sell out in a few minutes. I hate writing that because it sounds braggy, but I’m putting it out there just as fair warning. You’re going to have to act quick and maybe get lucky.

\n\n

If you want a ticket and wind up not getting one, there will be a live audio stream for everyone to listen to. This year we are not going to attempt to stream live video. Instead we’re going to work hard to get edited video of the event up on the web as soon as possible after the show is over. If you just can’t wait, listen to the live audio. If you want to see the show, wait for the video — it should be up some time on Wednesday at the latest.

\n\n

If you do get a ticket or already have one:

\n\n
    \n
  • All seats are general admission, with no distinction between orchestra level and the balcony.
  • \n
  • Put your ticket in Apple Wallet and bring ID to show at the door. (If you don’t have an iPhone (?) bring a copy of the ticket PDF.)
  • \n
  • Doors open at 6p, and there will be an open bar. Find a seat, grab a beverage, and mingle with your fellow fans of the show.
  • \n
  • The show itself should start at 7p.
  • \n
\n\n\n\n " - } - ] -} diff --git a/Frameworks/RSParser/RSParserTests/Resources/DaringFireball.rss b/Frameworks/RSParser/RSParserTests/Resources/DaringFireball.rss deleted file mode 100755 index ba3d1400a..000000000 --- a/Frameworks/RSParser/RSParserTests/Resources/DaringFireball.rss +++ /dev/null @@ -1,2278 +0,0 @@ - - -Daring Fireball -By John Gruber - - -http://daringfireball.net/feeds/main - -2016-02-28T21:06:52ZCopyright © 2016, John Gruber - Apple Product Event: Monday March 21 - - - - tag:daringfireball.net,2016:/linked//6.32173 - 2016-02-27T21:59:47Z - 2016-02-27T22:39:17Z - - John Gruber - http://daringfireball.net/ - - Kara Swisher, writing at Recode, broke the news:

- -
-

Attention Apple nerds, investors, media and everyone else who -needs to know when Tim Cook’s next product event is going to be -held: It’s going to be the week of March 21.

- -

Or to put it another way, it’s not going to be on March 15, the -time frame that other outlets previously reported, according to -several sources. It is not clear if the event was moved or if this -was the same timing as Apple had always planned.

-
- -

Swisher doesn’t have the exact date, although the <title> tag on her story reads “Apple Product Event Will Be Held March 22”. John Paczkowski (who usually gets these leaks first), confirms the week change, and says the event will be on Monday 21 March:

- -
-

Sources in position to know say the company has settled on March -21st as the date it will show off a handful of new products. These -people declined to say why Apple postponed the date by a week, but -it’s worth noting that it is one day prior to the company’s March -22 showdown with the government over a motion to compel it to help -hack the iPhone used by one of the San Bernardino terrorists.

-
- -

For what it’s worth, last year’s March event was on a Monday as well.

- -

Update: Jim Dalrymple:

- -
-

This sounds right to me.

-
- - - - ]]>
-
- Manuscripts and Findings - - - - tag:daringfireball.net,2016:/linked//6.32172 - 2016-02-27T00:11:11Z - 2016-02-27T00:13:11Z - - John Gruber - http://daringfireball.net/ - - My thanks to Nucleobytes for sponsoring this week’s DF RSS feed. Nucleobytes is a fascinating company. They specialize in creating Mac and iOS software for scientists and researchers, and they do it with great style — their apps have won multiple Apple Design Awards.

- -

Their latest creations are two apps for researchers, useful for anyone who researches anything from lab results, cooking recipes, or research for blog posts: Manuscripts and Findings.

- -
    -
  • Manuscripts is a writing tool that helps you concentrate on your story. Outline, plan and edit your project, insert figures, tables and math, then format citations using a killer workflow. Manuscripts supports both importing and exporting Markdown, Word, LaTeX, and HTML.

  • -
  • Findings is a lab notebook app that helps you keep a journal of your research, connected to notes, photos, and files. Plan your week, track progress, and share your findings with your colleagues or the world.

  • -
- -

Try the free basic versions, and use coupon DARINGFIREBALL for a special discount on the unlimited versions, this week only. (They have an even better offer for students.)

- - - - ]]>
-
- Donald Trump Vows to ‘Open Up’ Libel Laws - - - - tag:daringfireball.net,2016:/linked//6.32171 - 2016-02-26T21:47:27Z - 2016-02-26T21:47:28Z - - John Gruber - http://daringfireball.net/ - - Hadas Gold, writing for Politico:

- -
-

During a rally in Fort Worth, Texas, Trump began his usual tirade -against newspapers such as The New York Times and The Washington -Post, saying they’re “losing money” and are “dishonest.” The -Republican presidential candidate then took a different turn, -suggesting that when he’s president they’ll “have problems.”

- -

“One of the things I’m going to do if I win, and I hope we do and -we’re certainly leading. I’m going to open up our libel laws so -when they write purposely negative and horrible and false -articles, we can sue them and win lots of money. We’re going to -open up those libel laws. So when The New York Times writes a hit -piece which is a total disgrace or when The Washington Post, which -is there for other reasons, writes a hit piece, we can sue them -and win money instead of having no chance of winning because -they’re totally protected,” Trump said.

-
- -

Not worrisome at all. No sir.

- - - - ]]>
-
- Most Android Phones Are Not Encrypted - - - - tag:daringfireball.net,2016:/linked//6.32170 - 2016-02-26T17:43:11Z - 2016-02-28T21:06:52Z - - John Gruber - http://daringfireball.net/ - - Jose Pagliery, writing for CNN Money:

- -
-

Although 97% of Android phones have encryption as an option, less -than 35% of them actually got prompted to turn it on when they -first activated the phone. Even then, not everybody chooses that -extra layer of security.

- -

A Google spokesman said that encryption is now required for all -“high-performing devices” — like the Galaxy S7 — running the -latest version of Android, Marshmallow. But only 1.2% of Android -phones even have that version, according to Google.

- -

By comparison, most Apple products are uniformly secure: 94% of -iPhones run iOS 8 or 9, which encrypt all data. Apple (AAPL, -Tech30) makes its devices, designs the software, and retains full -control of the phone’s operating system.

- -

“If a person walks into a Best Buy and walks out with an iPhone, -it’s encrypted by default. If they walk out with an Android phone, -it’s largely vulnerable to surveillance,” said Christopher -Soghoian, the principal technologist at the American Civil -Liberties Union.

-
- -

Google is moving in the right direction, but here’s an area where the slow uptake of new versions of Android has a serious effect.

- - - - ]]>
-
- 9to5Mac: ‘Apple Likely to Drop the “5”, Call New 4-Inch Model the “iPhone SE”’ - - - - tag:daringfireball.net,2016:/linked//6.32169 - 2016-02-26T17:24:11Z - 2016-02-26T18:32:34Z - - John Gruber - http://daringfireball.net/ - - Mark Gurman:

- -
-

In January, we reported that Apple is preparing a new 4-inch -iPhone that is essentially 2013’s iPhone 5s with upgraded -internals. At the time, we heard that Apple would call the device -the “iPhone 5se” based on it being both an enhanced and “special -edition” version of the iPhone 5s. Now, we are hearing that Apple -appears to be going all in on the special edition factor: sources -say that Apple has decided to drop the “5” from the device’s name -and simply call it the “iPhone SE.” This will mark the first -iPhone upgrade without a number in its name and would logically -remove it from a yearly update cycle.

-
- -

A few points:

- -
    -
  • Apple was never going to call this phone the “5 SE”. I don’t know where Gurman got that, but that was never going to happen. Why would Apple give a new phone a name that makes it sound old?

  • -
  • Isn’t it more accurate to think of this as an iPhone 6S in a 4-inch body than as an iPhone 5S with “upgraded internals”? Other than the display, aren’t the “internals” the defining characteristics of any iPhone?

  • -
  • Dropping the number entirely fits with my theory that this phone is intended to remain on the market for 18-24 months.

  • -
- - - - ]]>
-
- Gogo Wi-Fi and Email Security - - - - tag:daringfireball.net,2016:/linked//6.32168 - 2016-02-26T17:12:34Z - 2016-02-26T19:00:17Z - - John Gruber - http://daringfireball.net/ - - Reporter Steven Petrow published a scary first-hand tale in USA Today, claiming that his email was hacked by another passenger on a Gogo-enabled flight. The implication was that you shouldn’t use email on Gogo unless you’re using a VPN.

- -

But Petrow’s email didn’t get intercepted because of some flaw with Gogo. It got intercepted because he wasn’t connecting to the POP or SMTP servers via SSL. In fact, his email provider, Earthlink, doesn’t even support SSL for email.

- -

Robert Graham at Errata Security explains:

- -
-

Early Internet stuff wasn’t encrypted, because encryption was -hard, and it was hard for bad guys to tap into wires to eavesdrop. -Now, with open WiFi hotspots at Starbucks or on the airplane, it’s -easy for hackers to eavesdrop on your network traffic. -Simultaneously, encryption has become a lot easier. All new -companies, those still fighting to acquire new customers, have -thus upgraded their infrastructure to support encryption. Stagnant -old companies, who are just milking their customers for profits, -haven’t upgraded their infrastructure.

- -

You see this in the picture below. Earthlink supports older -un-encrypted “POP3” (for fetching email from the server), but not -the new encrypted POP3 over SSL. Conversely, GMail doesn’t support -the older un-encrypted stuff (even if you wanted it to), but only -the newer encrypted version.

-
- -

Gogo is far from perfect, but it certainly wasn’t at fault in this case.

- -

Update: Like a lot of you, I’m not even sure I buy the whole story. Whole thing seems fishy.

- - - - ]]>
-
- Google, Facebook, Twitter, and Microsoft Plan to Support Apple - - - - tag:daringfireball.net,2016:/linked//6.32167 - 2016-02-25T22:56:47Z - 2016-02-25T22:56:48Z - - John Gruber - http://daringfireball.net/ - - Deepa Seetharaman and Jack Nicas, reporting for the WSJ:

- -
-

Several tech companies, including Google parent Alphabet Inc., -Facebook Inc. and Microsoft Corp., plan to file a joint motion -supporting Apple Inc. in its court fight against the Justice -Department over unlocking an alleged terrorist’s iPhone, according -to people familiar with the companies’ plans.

- -

At least one other tech company plans to be included in a joint -amicus brief next week generally supporting Apple’s position that -unlocking the iPhone would undermine tech firms’ efforts to -protect their users’ digital security, these people said. Twitter -Inc. also plans to support Apple in a motion, though it is unclear -if it will join the combined filing, another person familiar said.

- -

Microsoft President and Chief Legal Officer Brad Smith told -Congress on Thursday that his company would file a motion -supporting Apple.

-
- -

Nice.

- - - - ]]>
-
- Apple’s Motion to Vacate FBI Order - - - - tag:daringfireball.net,2016:/linked//6.32166 - 2016-02-25T20:24:56Z - 2016-02-25T20:25:28Z - - John Gruber - http://daringfireball.net/ - - A clear, cogent read. I often shy away from reading legal motions because they’re so often written in dense legalese, but this one is clear.

- -

This stuck out to me:

- -
-

Congress knows how to impose a duty on third parties to facilitate -the government’s decryption of devices. Similarly, it knows -exactly how to place limits on what the government can require of -telecommunications carriers and also on manufacturers of telephone -equipment and handsets. And in CALEA, Congress decided not to -require electronic communication service providers, like Apple, to -do what the government seeks here. Contrary to the government’s -contention that CALEA is inapplicable to this dispute, Congress -declared via CALEA that the government cannot dictate to providers -of electronic communications services or manufacturers of -telecommunications equipment any specific equipment design or -software configuration.

- -

In the section of CALEA entitled “Design of features and systems -configurations,” 47 U.S.C. § 1002(b)(1), the statute says that it -“does not authorize any law enforcement agency or officer —

- -
-

(1) to require any specific design of equipment, facilities, - services, features, or system configurations to be adopted by - any provider of a wire or electronic communication service, - any manufacturer of telecommunications equipment, or any - provider of telecommunications support services.

- -

(2) to prohibit the adoption of any equipment, facility, service, - or feature by any provider of a wire or electronic - communication service, any manufacturer of telecommunications - equipment, or any provider of telecommunications support - services.

-
-
- -

What Apple is arguing is that the All Writs Act is intended only to fill the gaps covering scenarios not covered by other laws, but CALEA (the Communications Assistance for Law Enforcement Act) is a law that was passed specifically to cover exactly this sort of scenario. This strikes me as a very compelling argument.

- - - - ]]>
-
- Microsoft Will File Amicus Brief Supporting Apple - - - - tag:daringfireball.net,2016:/linked//6.32165 - 2016-02-25T18:59:14Z - 2016-02-25T18:59:15Z - - John Gruber - http://daringfireball.net/ - - Dina Bass, reporting for Bloomberg:

- -
-

Microsoft Corp. backs Apple Inc. in its fight with the U.S. -government over unlocking a terrorist’s iPhone, said President and -Chief Legal Officer Brad Smith.

- -

The company will file an amicus brief to support Apple next week, -Smith said at a congressional hearing to discuss the need for new -legislation to govern privacy, security and law enforcement in the -age of Internet-based cloud services.

-
- -

Nice.

- - - - ]]>
-
- Apple to Tighten iCloud Backup Encryption - - - - tag:daringfireball.net,2016:/linked//6.32164 - 2016-02-25T18:02:44Z - 2016-02-25T18:02:45Z - - John Gruber - http://daringfireball.net/ - - Tim Bradshaw, reporting for the Financial Times:

- -
-

Apple is working on new ways to strengthen the encryption of -customers’ iCloud backups in a way that would make it impossible -for the company to comply with valid requests for data from law -enforcement, according to people familiar with its plans.

- -

The move would bolster Apple customers’ security against hackers -but also frustrate investigators who are currently able to obtain -data from Apple’s servers through a court order. Apple has -complied with thousands of such orders in the past.

- -

Developing such technology is in some ways more complex than -adding the kind of device-level security that Apple introduced to -the iPhone in 2014 with its iOS 8 update.

- -

Building new protections that mean Apple no longer has access to -iCloud encryption keys may inconvenience some customers. Such a -change would most likely mean that customers who forget their -iCloud password may be left unable to access their photos, -contacts and other personal information that is backed up to -Apple’s systems.

-
- - - - ]]>
-
- The Dangerous All Writs Act Precedent in the Apple Encryption Case - - - - tag:daringfireball.net,2016:/linked//6.32163 - 2016-02-25T17:07:13Z - 2016-02-25T17:07:15Z - - John Gruber - http://daringfireball.net/ - - Amy Davidson, writing for The New Yorker:

- -
-

It is essential to this story that the order to Apple is not a -subpoena: it is issued under the All Writs Act of 1789, which says -that federal courts can issue “all writs necessary or appropriate -in aid of their respective jurisdictions and agreeable to the -usages and principles of law.” Read as a whole, this simply means -that judges can tell people to follow the law, but they have to do -so in a way that, in itself, respects the law. The Act was written -at a time when a lot of the mechanics of the law still had to be -worked out. But there are qualifications there: warnings about the -writs having to be “appropriate” and “agreeable,” not just to the -law but to the law’s “principles.” The government, in its use of -the writ now, seems to be treating those caveats as background -noise. If it can tell Apple, which has been accused of no -wrongdoing, to sit down and write a custom operating system for -it, what else could it do?

-
- -

Lost amid the technical debate over encryption is the legal debate over this incredibly broad application of the All Writs Act.

- - - - ]]>
-
- Twitter’s Missing Manual - - - - tag:daringfireball.net,2016:/linked//6.32162 - 2016-02-25T16:45:49Z - 2016-02-25T16:45:50Z - - John Gruber - http://daringfireball.net/ - - Eevee:

- -
-

Here, then, is a list of all the non-obvious things about Twitter -that I know. Consider it both a reference for people who aren’t up -to their eyeballs in Twitter, and an example of how these hidden -features can pile up. I’m also throwing in a couple notes on -etiquette, because I think that’s strongly informed by the shape -of the platform.

-
- - - - ]]>
-
- Sharp Accepts Foxconn Takeover Bid - - - - tag:daringfireball.net,2016:/linked//6.32161 - 2016-02-25T05:21:30Z - 2016-02-25T17:46:42Z - - John Gruber - http://daringfireball.net/ - - Huge news for both companies. Interesting for Apple, too.

- -

Update:

- -
-

A deal to take over Japanese electronics giant Sharp by Taiwanese -manufacturer Foxconn, has been thrown into question by a last -minute delay.

- -

Foxconn said it had received new information from Sharp which -needed to be clarified.

-
- -

Whoops.

- - - - ]]>
-
- The Next Step in iPhone Impregnability - - - - tag:daringfireball.net,2016:/linked//6.32160 - 2016-02-25T03:26:27Z - 2016-02-25T04:35:17Z - - John Gruber - http://daringfireball.net/ - - Matt Apuzzo and Katie Benner, reporting for the NYT:

- -
-

Apple engineers have already begun developing new security -measures that would make it impossible for the government to break -into a locked iPhone using methods similar to those now at the -center of a court fight in California, according to people close -to the company and security experts.

- -

If Apple succeeds in upgrading its security — and experts say it -almost surely will — the company would create a significant -technical challenge for law enforcement agencies, even if the -Obama administration wins its fight over access to data stored on -an iPhone used by one of the killers in last year’s San -Bernardino, Calif., rampage. The F.B.I. would then have to find -another way to defeat Apple security, setting up a new cycle of -court fights and, yet again, more technical fixes by Apple. […]

- -

Apple built its recent operating systems to protect customer -information. As its chief executive, Timothy D. Cook, wrote in a -recent letter to customers, “We have even put that data out of our -own reach, because we believe the contents of your iPhone are none -of our business.”

- -

But there is a catch. Each iPhone has a built-in troubleshooting -system that lets the company update the system software without -the need for a user to enter a password. Apple designed that -feature to make it easier to repair malfunctioning phones.

-
- -

The way the iPhone works today, when put into recovery mode you can restore the operating system without entering the device passcode. The only restriction is that the version of iOS to be installed must be properly signed by Apple.

- -

I just tried it here with my old iPhone 6, which had been turned off for weeks. I powered it up, but did not unlock it. I put it in recovery mode, and then updated it to iOS 9.3 beta 4. Then it restarted. Now it’s running iOS 9.3 beta 4, and I still have not unlocked it. All my data is still on the phone — but it’s running a new version of iOS, without my having unlocked it.

- -

What the FBI wants Apple to do is create (and sign) a new version of iOS that they can force the San Bernardino suspect’s phone to install as an update — and this new version of iOS will allow them to easily brute-force the passcode.

- -

I think what Apple is leaking here is that they’re going to change this (perhaps as soon as this year’s new iPhone 7), so that you can’t install a new version of iOS, even in recovery mode, without entering the device’s passcode. (I think they will also do the same for firmware updates to the code that executes on the Secure Enclave — it will require a passcode lock.)

- -

If you do a full restore, you can install a new version of the OS without the passcode, but this wipes the data. See also: Activation Lock, which allows you to bypass the passcode to completely wipe an iPhone, but requires you to sign into iCloud before you can use it.

- - - - ]]>
-
- Scalia in 1987: ‘The Constitution Sometimes Insulates the Criminality of a Few in Order to Protect the Privacy of Us All’ - - - - tag:daringfireball.net,2016:/linked//6.32159 - 2016-02-25T02:53:17Z - 2016-02-25T02:53:18Z - - John Gruber - http://daringfireball.net/ - - NYT report on a 6-3 Supreme Court decision in 1987:

- -
-

Justice Scalia’s opinion was forcefully denounced as an -unjustified obstacle to law enforcement in dissenting opinions by -Associate Justices Sandra Day O’Connor and Lewis F. Powell Jr. -Chief Justice Rehnquist joined in both of the dissents.

- -

Justice Scalia, however, said, “There is nothing new in the -realization that the Constitution sometimes insulates the -criminality of a few in order to protect the privacy of us -all.” […]

- -

Justice Scalia’s majority opinion today said that although the -search for weapons was lawful — a shot had just been fired through -the floor of the apartment, injuring a man below — the police were -not justified in moving the stereo components even slightly to -check the serial numbers without “probable cause” to believe they -were stolen. He thus affirmed a ruling by an Arizona appellate -court that the stereo components, which turned out to have been -stolen in an armed robbery, could not be used as evidence against -the occupant of the apartment.

- -

Associate Justice William J. Brennan Jr., the Court’s senior -member, who is its leading liberal, apparently assigned Justice -Scalia to write the majority opinion, which he joined. Under the -Supreme Court’s procedures, the Chief Justice assigns opinions -when he is in the majority. When the Chief Justice dissents, as -in the Arizona case, the senior member of the majority has -assignment power.

-
- -

Conservative judges, as a general rule, tend to side with law enforcement in search and seizure cases. Scalia was certainly a conservative, but by no means was he in lockstep with them.

- - - - ]]>
-
- ABC News Posts Extensive Interview With Tim Cook on FBI/iPhone Case - - - - tag:daringfireball.net,2016:/linked//6.32158 - 2016-02-25T00:59:51Z - 2016-02-25T02:17:14Z - - John Gruber - http://daringfireball.net/ - - Solid, thorough, and I think very fair interview by David Muir. Cook made his case about as well as it could be made — a passionate defense of civil liberties. It’s 30 minutes long and worth every minute of it.

- - - - ]]>
-
- Former Bush Administration Official Argues Supreme Court Should Count Scalia’s Vote in Pending Cases - - - - tag:daringfireball.net,2016:/linked//6.32157 - 2016-02-24T22:46:11Z - 2016-02-24T22:46:12Z - - John Gruber - http://daringfireball.net/ - - This is how we get from here to there.

- - - - ]]>
-
- David Ortiz Makes a Final Plea to Yankees Fans - - - - tag:daringfireball.net,2016:/linked//6.32156 - 2016-02-24T22:02:58Z - 2016-02-24T22:17:46Z - - John Gruber - http://daringfireball.net/ - - Kevin Kernan, writing for the NY Post:

- -
-

When Ortiz, 40, makes his final Yankee Stadium appearance on Sept. -29, this is what he wants, and it speaks volumes about Ortiz the -player, the competitor, the enemy, the star.

- -

“You know what I want most of all?’’ Big Papi told The Post on -Tuesday at JetBlue Park. “I would love it if the fans at Yankee -Stadium gave me a standing ovation.’’

- -

That’s what he wants, and that would be the perfect tribute to -Ortiz, who owns 503 home runs.

-
- -

I would wholeheartedly join in that ovation. Great player, great rival, and his retirement really marks the end of the epic Yankees-Sox rivalry from the early 2000s. I would expect appearances from Derek Jeter, Mariano Rivera, Jorge Posada, and Joe Torre. Just thinking about it makes me want to buy tickets.

- - - - ]]>
-
- Spotify Moves Infrastructure to Google Cloud Platform - - - - tag:daringfireball.net,2016:/linked//6.32155 - 2016-02-24T03:01:04Z - 2016-02-24T03:01:06Z - - John Gruber - http://daringfireball.net/ - - You heard it here first: this presages Google acquiring Spotify. (I heard it from Om Malik first.)

- - - - ]]>
-
- Was Pew’s Polling Question on the Apple/FBI Debate Misleading? - - - - tag:daringfireball.net,2016:/linked//6.32154 - 2016-02-23T22:16:51Z - 2016-02-23T22:16:52Z - - John Gruber - http://daringfireball.net/ - - Mike Masnick, writing for TechDirt:

- -
-

The question asked was

- -
-

As you may know, RANDOMIZE: [the FBI has said that accessing the -iPhone is an important part of their ongoing investigation into -the San Bernardino attacks] while [Apple has said that unlocking -the iPhone could compromise the security of other users’ -information] do you think Apple [READ; RANDOMIZE]?

- -

(1) Should unlock the iPhone (2) Should not unlock the iPhone (3) - Don’t Know.

-
- -

But that’s not the issue in this case!

- -

As noted in the past, when it’s possible for Apple to get access -to data, it has always done so in response to lawful court orders. -That’s similar to almost every other company as well. This case is -different because it’s not asking Apple to “unlock the iPhone.” -The issue is that Apple cannot unlock the iPhone and thus, the FBI -has instead gotten a court order to demand that Apple create an -entirely new operating system that undermines the safety and -security of iPhones, so that the FBI can hack into the iPhone. -That’s a really different thing.

-
- -

He makes a good point. But when it comes to public polling on an issue like this, you can’t expect the public to understand the technical issues. Ideally, yes, the language used by Pew would have been much more precise. But basically what they were asking is “Do you think Apple should do whatever the FBI wants them to do to get the information from the San Bernardino suspect’s iPhone?” For polling purposes, I don’t think it matters much what “whatever” is.

- -

It’s true that if phrased differently, it’s quite possible you’d get a polling showing more support for Apple. But the bottom line is that a lot of Americans think Apple should just do what the FBI is asking them to do.

- - - - ]]>
-
- On Ribbons and Ribbon Cutters - - - - tag:daringfireball.net,2016:/linked//6.32153 - 2016-02-23T22:00:23Z - 2016-02-23T22:00:24Z - - John Gruber - http://daringfireball.net/ - - Jonathan Zdziarski (who has been killing it with his analysis of the Apple/FBI fight):

- -
-

With most non-technical people struggling to make sense of the -battle between FBI and Apple, Bill Gates introduced an excellent -analogy to explain cryptography to the average non-geek. Gates -used the analogy of encryption as a “ribbon around a hard drive”. -Good encryption is more like a chastity belt, but since Farook -decided to use a weak passcode, I think it’s fair here to call it -a ribbon. In any case, let’s go with Gates’s ribbon analogy. […]

- -

Instead of cutting the ribbon, which would be a much simpler task, -FBI is ordering Apple to invent a ribbon cutter — a forensic tool -capable of cutting the ribbon for FBI, and is promising to use it -on just this one phone. In reality, there’s already a line -beginning to form behind Comey should he get his way.

-
- - - - ]]>
-
- Apple to Restore UI Navigation With Pencil in Next iOS 9.3 Beta - - - - tag:daringfireball.net,2016:/linked//6.32152 - 2016-02-23T21:37:34Z - 2016-02-23T22:26:45Z - - John Gruber - http://daringfireball.net/ - - That didn’t take long. Apple, in a statement to iMore and a few other publications:

- -
-

Apple Pencil has been a huge hit with iPad Pro users, who love it -for drawing, annotating and taking notes,” an Apple spokesperson -told iMore. “We believe a finger will always be the primary way -users navigate on an iPad, but we understand that some customers -like to use Apple Pencil for this as well and we’ve been working -on ways to better implement this while maintaining compatibility -during this latest beta cycle. We will add this functionality back -in the next beta of iOS 9.3.

-
- -

One thing I take away from the vocal reaction to this: the Apple Pencil and iPad Pro have passionate users.

- - - - ]]>
-
- Apple vs. FBI: ‘Just This Once’? - - - - tag:daringfireball.net,2016:/linked//6.32151 - 2016-02-23T21:20:18Z - 2016-02-23T21:20:19Z - - John Gruber - http://daringfireball.net/ - - Julian Sanchez, writing for Just Security:

- -
-

Consider: Possibly the next iPhone simply eliminates Apple’s -ability to assist in any way. But it’s hard to imagine a -scenario where the designer and key-holder for a device designed -to be used by normal humans can do literally nothing, at the -margin, to assist an attacker. That means every improvement in -device security involves a gamble: Maybe the cost of developing -new ways to attack the newly hardened device becomes so high that -the courts recognize it as an “undue burden” and start quashing -(or declining to issue) All Writs Act orders to compel hacking -assistance. Maybe. But Apple is a very large, very rich company, -and much of the practical “burden” comes from the demands of -complying securely and at scale. The government will surely -continue arguing in future cases that the burden of complying -just this one time are not so great for a huge tech company like -Apple. (And, to quote The Smiths, they’ll never never do it -again — of course they won’t; not until the next time.)

-
- -

Sanchez makes an interesting point here about Apple being disincentivized from improving iPhone security if they lose this case. Imagine if Apple made safes, but the government could compel them to crack their own safes under warrant. The harder they make these safes to crack, the more work they bring upon themselves when compelled to crack them.

- -

I don’t think Apple would succumb to that and stop improving their device security, but it shows what an untenable position the government is trying to put Apple in. The only easy way out for Apple, if they lose, is to stop making iPhones truly secure.

- - - - ]]>
-
- High-Profile Attorney Ted Olson Joins Apple’s Fight Against FBI Terror Probe - - - - tag:daringfireball.net,2016:/linked//6.32150 - 2016-02-23T20:49:09Z - 2016-02-23T21:11:40Z - - John Gruber - http://daringfireball.net/ - - Taylor Goldenstein, reporting for the LA Times:

- -
-

Olson and Theodore J. Boutrous Jr. are the attorneys of record -representing Apple, according to a court filing. Boutrous and -Olson worked together to fight California’s previous ban on -same-sex marriage.

- -

Olson is best known for successfully arguing on behalf of George -W. Bush in the Supreme Court case Bush vs. Gore, which decided the -2000 presidential election, and for challenging California’s -Proposition 8, the measure that banned gay marriage, before the -Supreme Court.

-
- -

Olson is truly an extraordinary figure, both in terms of his career (winning landmark cases for conservatives, like Bush v. Gore and Citizens United; then winning the case that legalized gay marriage nationwide), and his personal life (his wife was a passenger on the plane that crashed into the Pentagon on 9/11).

- - - - ]]>
-
- iOS 9.3 Betas Remove the Ability to Navigate iPad UI With Apple Pencil - - - - tag:daringfireball.net,2016:/linked//6.32149 - 2016-02-23T19:24:01Z - 2016-02-23T21:39:01Z - - John Gruber - http://daringfireball.net/ - - Serenity Caldwell, at iMore:

- -
-

Unfortunately, whether by bug or intentional design, the Pencil’s -navigational prowess appears to have vanished in the iOS 9.3 -public betas. With 9.3, you can no longer scroll or manipulate -text; the only places the Pencil works are on canvas or when -pressing digital buttons.

- -

Normally, I don’t write about beta bugs and features, because it’s -a beta: There are always bugs, and features change. But this -functionality is important enough that I wanted to talk about it -before Apple submits its final 9.3 release. It could be a bug, -yes: But several betas in, we’ve seen fixes for Smart Connector -keyboards and new features, and the Pencil remains crippled. Which -makes me think, more and more, that this is a conscious decision -on the part of Apple’s engineering team. (I did reach out to the -company about the issue, and will update if and when I receive a -response.)

-
- -

Myke Hurley and CGP Grey talk about this on the latest episode of their podcast, Cortex. Grey says:

- -
-

Sources in the know confirm that removing the functionality of the -Apple Pencil is a decision inside of Apple. It is not a bug they -have overlooked for three betas. It is a decision.

-
- -

My only guess as to why Apple would change this is that they want to enable you to scroll/pan (with your finger) while drawing/marking-up with the Pencil. If so, the mistake wasn’t making this change in iOS 9.3 — the mistake was allowing the Pencil to control the UI in the first place.

- -

I hate to say it, but now that iPad Pro users have gotten used to using the Pencil to navigate the UI, maybe it should be a setting? Maybe under Accessibility? Grey, for example, says using the Pencil to navigate the UI helps him avoid RSI pain.

- -

Update, two hours later: Apple has told The Verge that UI navigation via Pencil will return in the next iOS 9.3 beta.

- - - - ]]>
-
- Bill Gates Breaks Ranks Over FBI Apple Request - - - - tag:daringfireball.net,2016:/linked//6.32148 - 2016-02-23T19:00:59Z - 2016-02-23T19:45:52Z - - John Gruber - http://daringfireball.net/ - - Stephen Foley and Tim Bradshaw, writing for The Financial Times:

- -
-

“This is a specific case where the government is asking for -access to information. They are not asking for some general -thing, they are asking for a particular case,” Mr Gates told the -Financial Times.

- -

“It is no different than [the question of] should anybody ever -have been able to tell the phone company to get information, -should anybody be able to get at bank records. Let’s say the bank -had tied a ribbon round the disk drive and said, ‘Don’t make me -cut this ribbon because you’ll make me cut it many times’.”

-
- -

Gates is so smart — surely he understands that if the FBI prevails, this will set precedent that will be used again and again. It seems to me he’s arguing that we should not be allowed to have devices protected by strong encryption.

- -

Update: Gates said today he thinks the FT mischaracterized his position, but I’m not really seeing it. He certainly isn’t siding with Apple — his stance seems, at best, lukewarm, like Sundar Pichai’s.

- - - - ]]>
-
- Poll Shows More Support for Justice Department Than for Apple - - - - tag:daringfireball.net,2016:/linked//6.32147 - 2016-02-23T18:54:44Z - 2016-02-24T03:22:36Z - - John Gruber - http://daringfireball.net/ - - Pew Research Center:

- -
-

As the standoff between the Department of Justice and Apple Inc. -continues over an iPhone used by one of the suspects in the San -Bernardino terrorist attacks, 51% say Apple should unlock the -iPhone to assist the ongoing FBI investigation. Fewer Americans -(38%) say Apple should not unlock the phone to ensure the security -of its other users’ information; 11% do not offer an opinion on -the question.

- -

News about a federal court ordering Apple to unlock the suspect’s -iPhone has registered widely with the public: 75% say they have -heard either a lot (39%) or a little (36%) about the situation.

-
- -

This is exactly why Apple’s stance on this issue is so commendable. They’re doing what they believe to be right, even though it is unpopular.

- - - - ]]>
-
- WSJ: ‘Justice Department Seeks to Force Apple to Extract Data From About 12 Other iPhones’ - - - - tag:daringfireball.net,2016:/linked//6.32146 - 2016-02-23T18:53:40Z - 2016-02-23T18:53:41Z - - John Gruber - http://daringfireball.net/ - - Devlin Barrett, reporting for the WSJ:

- -
-

The Justice Department is pursuing court orders to make Apple Inc. -help investigators extract data from iPhones in about a dozen -undisclosed cases around the country, in disputes similar to the -current battle over a terrorist’s locked phone, according to a -newly-unsealed court document.

- -

The other phones are evidence in cases where prosecutors have -sought, as in the San Bernardino, Calif., terror case, to use an -18th-century law called the All Writs Act to compel the company to -help them bypass the passcode security feature of phones that may -hold evidence, according to a letter from Apple which was unsealed -in Brooklyn federal court Tuesday. […]

- -

The letter doesn’t describe the specific types of criminal -investigations related to those phones, but people familiar with -them said they don’t involve terrorism cases. The 12 cases remain -in a kind of limbo amid the bigger, more confrontational legal -duel between the government and the company over an iPhone seized -in the terror case in California, these people said.

-
- -

But it’s really just about that one, single iPhone in the San Bernardino case.

- - - - ]]>
-
- ‘Absolutely Right’ - - - - tag:daringfireball.net,2016:/linked//6.32145 - 2016-02-23T18:36:27Z - 2016-02-23T18:39:51Z - - John Gruber - http://daringfireball.net/ - - Katie Benner and Matt Apuzzo, reporting for the NYT on whether the FBI’s request for Apple to unlock the San Bernardino shooter’s iPhone will open the door to more such requests:

- -
-

In a note posted to its website on Monday, Apple reiterated that -the government’s request seems narrow but really isn’t. “Law -enforcement agents around the country have already said they have -hundreds of iPhones they want Apple to unlock if the F.B.I. wins -this case,” the company said.

- -

To that point, the New York City police commissioner, William J. -Bratton, and the Manhattan district attorney, Cyrus R. Vance Jr., -criticized Apple after it refused to comply with the court order -and said that they currently possessed 175 iPhones that they could -not unlock.

- -

Charlie Rose recently interviewed Mr. Vance and asked if he would -want access to all phones that were part of a criminal proceeding -should the government prevail in the San Bernardino case.

- -

Mr. Vance responded: “Absolutely right.”

-
- - - - ]]>
-
- Mark Zuckerberg Stole Samsung’s Galaxy S7 Show - - - - tag:daringfireball.net,2016:/linked//6.32144 - 2016-02-23T03:46:31Z - 2016-02-23T03:46:32Z - - John Gruber - http://daringfireball.net/ - - Interesting marriage of convenience. Samsung has hardware but no interesting software. Facebook has interesting software but no hardware.

- - - - ]]>
-
- MDM Software Would Have Unlocked San Bernardino Shooter’s iPhone - - - - tag:daringfireball.net,2016:/linked//6.32143 - 2016-02-23T01:18:25Z - 2016-02-23T01:18:27Z - - John Gruber - http://daringfireball.net/ - - CBS News:

- -
-

If the technology, known as mobile device management, had been -installed, San Bernardino officials would have been able to -remotely unlock the iPhone for the FBI without the theatrics of a -court battle that is now pitting digital privacy rights against -national security concerns.

- -

The service costs $4 per month per phone.

- -

Instead, the only person who knew the unlocking passcode for the -phone is the dead gunman, Syed Farook, who worked as an inspector -in the county’s public health department.

-
- -

I had assumed they weren’t using MDM, but it’s good to have confirmation.

- - - - ]]>
-
- FBI Director James Comey Publishes Op-Ed on Apple/Encryption Case - - - - tag:daringfireball.net,2016:/linked//6.32141 - 2016-02-22T21:52:48Z - 2016-02-22T21:52:49Z - - John Gruber - http://daringfireball.net/ - - James Comey, in a brief op-ed published last night by Lawfare:

- -
-

The San Bernardino litigation isn’t about trying to set a -precedent or send any kind of message. It is about the victims and -justice. Fourteen people were slaughtered and many more had their -lives and bodies ruined. We owe them a thorough and professional -investigation under law. That’s what this is. The American people -should expect nothing less from the FBI.

-
- -

It is very difficult to take Comey’s opening sentence seriously. Everyone — on both sides of the issues — knows that this is about setting precedent.

- -
-

The particular legal issue is actually quite narrow. The relief we -seek is limited and its value increasingly obsolete because the -technology continues to evolve. We simply want the chance, with a -search warrant, to try to guess the terrorist’s passcode without -the phone essentially self-destructing and without it taking a -decade to guess correctly. That’s it. We don’t want to break -anyone’s encryption or set a master key loose on the land. I hope -thoughtful people will take the time to understand that. Maybe the -phone holds the clue to finding more terrorists. Maybe it doesn’t. -But we can’t look the survivors in the eye, or ourselves in the -mirror, if we don’t follow this lead.

-
- -

This is a purely emotional appeal. By Comey’s logic here, FBI agents should be considered above the law, able to pursue any and every avenue possible in the pursuit of information in a case with high stakes. That’s not how our system works. We are governed by the rule of law. Encryption is legal.

- -

Ultimately, that is where Comey and the FBI are going to take this. They’re going to try to make strong encryption illegal.

- - - - ]]>
-
- In Internal Email, Apple CEO Tim Cook Says Refusal to Unlock iPhone Is an Issue of Civil Liberties - - - - tag:daringfireball.net,2016:/linked//6.32140 - 2016-02-22T21:07:23Z - 2016-02-22T21:07:24Z - - John Gruber - http://daringfireball.net/ - - Tim Cook, in a company-wide memo:

- -
-

Apple is a uniquely American company. It does not feel right to be -on the opposite side of the government in a case centering on the -freedoms and liberties that government is meant to protect.

- -

Our country has always been strongest when we come together. We -feel the best way forward would be for the government to -withdraw its demands under the All Writs Act and, as some in -Congress have proposed, form a commission or other panel of -experts on intelligence, technology and civil liberties to -discuss the implications for law enforcement, national security, -privacy and personal freedoms. Apple would gladly participate in -such an effort.

-
- - - - ]]>
-
- Apple Publishes FAQ on Their Fight Against the FBI - - - - tag:daringfireball.net,2016:/linked//6.32139 - 2016-02-22T21:06:12Z - 2016-02-22T21:06:13Z - - John Gruber - http://daringfireball.net/ - - Cogent.

- - - - ]]>
-
- - - -tag:daringfireball.net,2016:/feeds/sponsors//11.32142 - - Daring Fireball Department of Commerce - - -2016-02-22T20:11:01-05:00 -2016-02-22T20:11:03-05:00 - -Everybody does research. Whether you investigate gravitational waves, do fact-finding for blog posts, study the genetics of Rhagoletis pomonella, or design the next revolutionary cupcake recipe, you are a researcher.

- -

Research needs great tools — that’s where we come in! We build innovative Mac and iOS apps for researchers, that even got us several Apple Design Awards. Today, we present our latest creations: Manuscripts and Findings.

- -
    -
  • Manuscripts is a writing tool that helps you concentrate on your story. Outline, plan and edit your project, insert figures, tables and math, then format citations using a killer workflow. Import and export of Markdown, Word, LaTeX and HTML is included.

  • -
  • Findings is a lab notebook app that helps you keep a journal of your research, connected to notes, photos and files. Plan your week, track progress, and share your findings with your colleagues… or the world.

  • -
- -

Try the free basic versions, and use coupon DARINGFIREBALL for a special discount on the unlimited versions, this week only. We hope you’ll like Manuscripts & Findings!

- -]]>
-[Sponsor] Manuscripts and Findings: Your Research Is Ready for a Big Upgrade
- The Talk Show: ‘iTools or Whatever’ - - - - tag:daringfireball.net,2016:/linked//6.32138 - 2016-02-21T23:15:40Z - 2016-02-21T23:15:43Z - - John Gruber - http://daringfireball.net/ - - For your enjoyment, a new episode of my podcast, with special guest Jim Dalrymple. Topics include the Apple/FBI legal showdown, the debate over Apple software quality, and more.

- -

Sponsored by:

- -
    -
  • Squarespace: Build it beautiful. Use code GRUBER for 10% off your first order.
  • -
  • Fracture: Photos printed in vivid color directly on glass. Use promo code TALKSHOW10 for 10% off your first order.
  • -
  • Harry’s: An exceptional shave at a fraction of the price. Use code TALKSHOW for $5 off your first purchase.
  • -
- - - - ]]>
-
- - - - tag:daringfireball.net,2016://1.32137 - 2016-02-21T22:05:40Z - 2016-02-21T22:45:23Z - - John Gruber - http://daringfireball.net/ - -The key point is that you do not have to unlock an iPhone to have it back up to iCloud. But a locked iPhone can’t back up to iCloud if the associated Apple ID password has been changed.

-]]>
- The latest news in the Apple-FBI legal fight has resulted in much confusion. John Paczkowski, reporting for BuzzFeed:

- -
-

The FBI has claimed that the password was changed by someone at -the San Bernardino Health Department. Friday night, however, -things took a further turn when the San Bernardino County’s -official Twitter account stated, “The County was working -cooperatively with the FBI when it reset the iCloud password at -the FBI’s request.”

- -

County spokesman David Wert told BuzzFeed News on Saturday -afternoon the tweet was an authentic statement, but he had nothing -further to add.

- -

The Justice Department did not respond to requests for comment on -Saturday; an Apple spokesperson said the company had no additional -comment beyond prior statements.

-
- -

Here is what the FBI wrote in its legal motion, in a footnote on the four ways Apple suggested they obtain the data they seek:

- -
-

(3) to attempt an auto-backup of the SUBJECT DEVICE with the - related iCloud account (which would not work in this case - because neither the owner nor the government knew the password - the iCloud account, and the owner, in an attempt to gain - access to some information in the hours after the attack, was - able to reset the password remotely, but that had the effect - of eliminating the possibility of an auto-backup);

-
- -

To unpack this, the “owner” is not Syed Farook, the shooter. The iPhone at the center of this was supplied by Farook’s employer, the San Bernardino County Department of Public Health. They are the “owner”. The “government” is the federal government: the FBI and the Department of Justice.

- -

The iPhone had been configured to back up to iCloud. However, at the time of the attack, it had not been backed up to iCloud for six weeks. Under warrant, Apple supplied the FBI with the data from that six-week-old backup. The FBI (for obvious reasons) would like the most recent six weeks of data from the phone, too.1

- -

iCloud backups are triggered automatically when the phone is (a) on a known Wi-Fi network, and (b) plugged-in to power. Apple’s suggestion to the FBI was that if they took the iPhone to Farook’s office and plugged it in, it might trigger a backup. If that had worked, Apple could supply the FBI with the contents of that new backup, including the most recent six weeks of data.

- -

It is not clear to me from any of the reports I have read why the iPhone had not been backed up in six weeks. It’s possible that Farook had disabled iCloud backups, in which case this whole thing is moot.2 But it’s also possible the only reason the phone hadn’t been backed up in six weeks is that it had not been plugged-in while on a known Wi-Fi network in six weeks. The phone would have to be unlocked to determine this, and the whole point of this fight is that the phone can’t be unlocked.

- -

The FBI screwed this up by directing the San Bernardino County Department of Public Health to reset Farook’s Apple ID password. They did not, and apparently could not, change anything on the phone itself. But once they reset the Apple ID password, the phone could not back up to iCloud, because the phone needed to be updated with the newly-reset Apple ID password — and they could not do that because they can’t unlock the phone.

- -

The key point is that you do not have to unlock an iPhone to have it back up to iCloud. But a locked iPhone can’t back up to iCloud if the associated Apple ID password has been changed.

- -

Again, there are two password-type things at play here. The Apple ID (iCloud) password, and the four-digit device passcode locking the iPhone. The county, at the behest of the FBI, reset the Apple ID password. This did not allow them to unlock the iPhone, and, worse, it prevented the iPhone from initiating a new backup to iCloud.

- -

How did the county reset Farook’s Apple ID password? We don’t know for sure, but the most likely answer is that if his Apple ID was his work-issued email account, then the IT department at the county could go to iforgot.apple.com, enter Farook’s work email address, and then access his email account to click the confirmation URL to reset the password.

- -

In short:

- -
    -
  • The data the FBI claims to want is on Farook’s iPhone.
  • -
  • They already have access to his iCloud account.
  • -
  • They might have been able to transfer the data on his iPhone to his iCloud account via an automated backup, but they can’t because they reset his Apple ID (iCloud) password.
  • -
- -

The only possible explanations for this are incompetence or dishonesty on the part of the FBI. Incompetence, if they didn’t realize that resetting the Apple ID password could prevent the iPhone from backing up to iCloud. Dishonesty, if they directed the county to do this knowing the repercussions, with the goal of setting up this fight to force Apple to create a back door for them in iOS. I’m not sure which to believe at this point. I’d like to know exactly when this directive to reset the Apple ID password was given — ” in the hours after the attack” leaves a lot of wiggle room.

- -
-
-
    -
  1. -

    Much (or all?) of the data stored on Apple’s iCloud backup servers is not encrypted. Or, if it is encrypted, it is encrypted in a way that Apple can decrypt. Apple has a PDF that describes the information available to U.S. law enforcement from iCloud, but to me it’s not clear exactly what is available under warrant. I would bet a large sum of money that Apple is hard at work on an iCloud backup system that does store data encrypted in a way that Apple cannot read it without the user’s Apple ID password. ↩︎

    -
  2. -
  3. -

    Another possibility: Farook’s iCloud storage was full. If this were the case, presumably Apple could have granted his account additional storage to allow a fresh backup to occur. But again, this became moot as soon as the county reset the Apple ID password at the behest of the FBI. ↩︎︎

    -
  4. -
-
- - - - ]]>
- ★ On the San Bernardino Suspect’s Apple ID Password Reset
- White House Petition to Side With Apple in FBI Fight - - - - tag:daringfireball.net,2016:/linked//6.32136 - 2016-02-21T21:38:07Z - 2016-02-21T21:38:09Z - - John Gruber - http://daringfireball.net/ - - I don’t have high hopes for this (the Obama administration seems hopelessly tied to law enforcement on this subject), but I signed:

- -
-

The FBI, is demanding that Apple build a “backdoor” to bypass -digital locks protecting consumer information on Apple’s popular -iPhones.

- -

We the undersigned, oppose this order, which has implications far -beyond the legal case at hand.

-
- - - - ]]>
-
- New York Times Publishes Report on iPhone Security and China - - - - tag:daringfireball.net,2016:/linked//6.32135 - 2016-02-20T22:36:43Z - 2016-02-20T22:39:02Z - - John Gruber - http://daringfireball.net/ - - Katie Benner and Paul Mozer, reporting for the NYT and revisiting the topic excised from a report earlier this week:

- -
-

In China, for example, Apple — like any other foreign company -selling smartphones — hands over devices for import checks by -Chinese regulators. Apple also maintains server computers in -China, but Apple has previously said that Beijing cannot view the -data and that the keys to the servers are not stored in China. In -practice and according to Chinese law, Beijing typically has -access to any data stored in China.

- -

If Apple accedes to American law enforcement demands for opening -the iPhone in the San Bernardino case and Beijing asks for a -similar tool, it is unlikely Apple would be able to control -China’s use of it. Yet if Apple were to refuse Beijing, it would -potentially face a battery of penalties.

- -

Analysts said Chinese officials were pushing for greater control -over the encryption and security of computers and phones sold in -the country, though Beijing last year backed off on some proposals -that would have required foreign companies to provide encryption -keys for devices sold in the country after facing pressure from -foreign trade groups.

- -

“People tend to forget the global impact of this,” said Raman Jit -Singh Chima, policy director at Access Now, a nonprofit that works -for Internet freedoms. “The reality is the damage done when a -democratic government does something like this is massive. It’s -even more negative in places where there are fewer freedoms.”

-
- -

Another way to look at this is a choice between the lesser of two evils. Is it a bad thing if law enforcement loses access to the contents of cell phones as state of the art for security increases? Yes. But it would be far, far worse — for entirely different reasons — if we eliminate true security by mandating back doors.

- - - - ]]>
-
- San Bernardino Officials: Apple ID Password for Terrorist’s iPhone Reset at FBI Request - - - - tag:daringfireball.net,2016:/linked//6.32134 - 2016-02-20T22:14:38Z - 2016-02-20T23:45:31Z - - John Gruber - http://daringfireball.net/ - - This story keeps getting weirder. John Paczkowski, at BuzzFeed:

- -
-

The FBI has claimed that the password was changed by someone at -the San Bernardino Health Department. Friday night, however, -things took a further turn when the San Bernardino County’s -official Twitter account stated, “The County was working -cooperatively with the FBI when it reset the iCloud password at -the FBI’s request.”

- -

County spokesman David Wert told BuzzFeed News on Saturday -afternoon the tweet was an authentic statement, but he had nothing -further to add.

- -

The Justice Department did not respond to requests for comment on -Saturday; an Apple spokesperson said the company had no additional -comment beyond prior statements.

-
- -

The additional wrinkle here is that when the FBI first revealed this, in this footnote (screenshot) of their legal motion (whole motion linked above, on “claimed”), they strongly implied that the San Bernardino Health Department did this on their own, like they were a bunch of yokels who panicked and did the wrong thing. Instead, it turns out, they were following the FBI’s instructions.

- -

The FBI says this happened “in the hours after the attack”. My question: How many hours?

- - - - ]]>
-
- DevMate by MacPaw - - - - tag:daringfireball.net,2016:/linked//6.32133 - 2016-02-20T00:23:57Z - 2016-02-20T00:23:58Z - - John Gruber - http://daringfireball.net/ - - My thanks to MacPaw for sponsoring this week’s DF RSS feed to announce that their developer platform DevMate is now available free of charge. DevMate is a single SDK that provides a slew of back-end services for Mac developers: in-app purchasing, software licensing, update delivery, crash reports, user feedback, and more. Plus real-time analytics, with sales and downloads, are available from DevMate’s dashboard.

- -

Among the indie Mac developers using DevMate for their apps are MacPaw themselves (for CleanMyMac), Smile Software, and Realmac. It’s a robust, dependable solution for developers who want to sell their Mac apps outside the App Store.

- - - - ]]>
-
- More Mac App Store Certificate Problems - - - - tag:daringfireball.net,2016:/linked//6.32132 - 2016-02-20T00:12:10Z - 2016-02-20T00:19:07Z - - John Gruber - http://daringfireball.net/ - - Lost amid the FBI/iPhone encryption hubbub was another bad week for the Mac App Store — apps just stopped launching, with the only solution being to delete the app(s) and re-install from the store. Michael Tsai (as usual) compiled a thorough roundup of information and commentary.

- - - - ]]>
-
- - - - tag:daringfireball.net,2016://1.32054 - 2016-02-04T01:28:15Z - 2016-02-04T16:33:43Z - - John Gruber - http://daringfireball.net/ - -Maybe we expect too much. But Apple’s hardware doesn’t have little problems like this.

-]]>
- Following up on Walt Mossberg’s column regarding the quality of Apple’s first-party apps, Jim Dalrymple writes:

- -
-

I understand that Apple has a lot of balls in the air, but they -have clearly taken their eye off some of them. There is absolutely -no doubt that Apple Music is getting better with each update to -the app, but what we have now is more of a 1.0 version than what -we received last year.

- -

Personally, I don’t care much about all the celebrities that Apple -can parade around — I care about a music service that works. -That’s it.

- -

If Apple Music (or any of the other software that has -problems) was the iPhone, it would never have been released in -the state it was.

-
- -

Software and hardware are profoundly different disciplines, so it’s hard to compare them directly. But it seems obvious to me that Apple, institutionally, has higher standards for hardware design and quality than it does for software.

- -

Maybe this is the natural result of the fact hardware standards must be high, because they can’t issue “hardware updates” over the air like they can with software. But the perception is now widespread that the balance between Apple’s hardware and software quality has shifted in recent years. I see a lot of people nodding their heads in agreement with Mossberg and Dalrymple’s pieces today.

- -

We went over this same ground a year ago in the wake of Marco Arment’s “Apple Has Lost the Functional High Ground”, culminating in a really interesting (to me at least) discussion with Phil Schiller at my “Live From WWDC” episode of The Talk Show. That we’re still talking about it a year later — and that the consensus reaction is one of agreement — suggests that Apple probably does have a software problem, and they definitely have a perception problem.

- -

I’ll offer a small personal anecdote. Overall I’ve had great success with iCloud Photo Library. I’ve got over 18,000 photos and almost 400 videos. And I’ve got a slew of devices — iPhones, iPads, and Macs — all using the same iCloud account. And those photos are available from all those devices. Except, a few weeks ago, I noticed that on my primary Mac, in Photos, at the bottom of the main “Photos” view, where it tells you exactly how many photos and videos you have, it said “Unable to Upload 5 Items”. Restarting didn’t fix it. Waiting didn’t fix it. And clicking on it didn’t do anything — I wanted to know which five items couldn’t be uploaded, and why. It seems to me that anybody in this situation would want to know those two things. But damned if Photos would tell me.

- -

Eventually, I found this support thread which suggested a solution: you can create a Smart Group in Photos using “Unable to upload to iCloud Photo Library” as the matching condition. Bingo: five items showed up. (Two of them were videos for which the original files couldn’t be found; three of them were duplicates of photos that were already in my library.)

- -

My little iCloud Photo Library syncing hiccup was not a huge deal — I was even lucky insofar as the two videos that couldn’t be found were meaningless. And I managed to find a solution. But it feels emblematic of the sort of nagging software problems people are struggling with in Apple’s apps. Not even the bug itself that led to these five items being unable to upload, but rather the fact that Photos knew about the problem but wouldn’t tell me the details I needed to fix it without my resorting to the very much non-obvious trick of creating a Smart Group to identify them. For me at least, “silent failure” is a big part of the problem — almost everything related to the whole discoveryd/mDNSresponder fiasco last year was about things that just silently stopped working.

- -

Maybe we expect too much from Apple’s software. But Apple’s hardware doesn’t have little problems like this.

- - - - ]]>
- ★ Apple’s App Problem
- - - - tag:daringfireball.net,2016://1.31994 - 2016-01-21T00:00:17Z - 2016-01-21T00:18:18Z - - John Gruber - http://daringfireball.net/ - -A year ago Apple sold 75 million iPhones in the fourth quarter of calendar 2015. There is no facility in the U.S. that can do that. There might not be anywhere in the world other than China that can operate at that sort of scale.

-]]>
- Arik Hesseldahl, writing for Recode on Donald Trump’s “we’re gonna get Apple to start building their damn computers and things in this country, instead of in other countries” campaign promise:

- -
-

Any honest presidential candidate regardless of party should say -clearly and indeed proudly that America doesn’t want these jobs to -come back. Final assembly jobs are low-skilled, low-paying -occupations; no American would wish to support a family on what -the jobs would pay. Workers at China’s Foxconn, which -manufacturers the iPhone, make about $402 per month after three -months of on-the-job probation. Even at the lowest minimum wage in -the U.S. — $5.15 an hour in Wyoming — American workers can’t -beat that.

-
- -

It’s not that simple. These jobs are certainly menial, but they’re not low-skill. As Tim Cook said on 60 Minutes:

- -
-

Charlie Rose: So if it’s not wages, what is it?

- -

Tim Cook: It’s skill. […]

- -

Charlie Rose: They have more skills than American workers? They -have more skills than —

- -

Tim Cook: Now — now, hold on.

- -

Charlie Rose: — German workers?

- -

Tim Cook: Yeah, let me — let me — let me clear, China put an -enormous focus on manufacturing. In what we would call, you and I -would call vocational kind of skills. The U.S., over time, began -to stop having as many vocational kind of skills. I mean, you can -take every tool and die maker in the United States and probably -put them in a room that we’re currently sitting in. In China, you -would have to have multiple football fields.

- -

Charlie Rose: Because they’ve taught those skills in their -schools?

- -

Tim Cook: It’s because it was a focus of them — it’s a focus of -their educational system. And so that is the reality.

-
- -

Wages are a huge factor, but for the sake of argument, let’s say Apple was willing to dip into its massive cash reserves and pay assembly line workers in the U.S. a good wage. Where would these U.S.-made iPhone be assembled? A year ago Apple sold 75 million iPhones in the fourth quarter of calendar 2014. There is no facility in the U.S. that can do that. There might not be anywhere in the world other than China that can operate at that sort of scale. That’s almost one million iPhones per day. 10 iPhones per second. Think about that.

- -

You can say, well, Apple could dig even deeper into its coffers and build such facilities. And train tens of thousands of employees. But why would they? Part of the marvel of Apple’s operations is that they can assemble and sell an unfathomable number of devices but they’re not on the hook for the assembly plants and facilities. When iPhones go the way of the iPod in 10 or 15 or 20 years, Apple doesn’t have any factories to close or convert for other uses. Foxconn does.

- -

The U.S. can’t compete with China on wages. It can’t compete on the size of the labor force. China has had a decades-long push in its education system to train these workers; the U.S. has not. And the U.S. doesn’t have the facilities or the proximity to the Asian component manufacturers.

- -

The only way Apple could ever switch to U.S. assembly and manufacturing would be if they automated the entire process — to build machines that build the machines. That, in fact, is what NeXT did while they were in the hardware business. But NeXT only ever sold about 50,000 computers total. Apple needed to assemble 35,000 iPhones per hour last year.

- -

So long as assembling these devices remains labor intensive, it has to happen in China. And if someday it becomes automated — if the machines are built by machines — by definition it’s not going to create manufacturing jobs.1

- -
-
-
    -
  1. -

    I do wonder about the purported Apple car. Would that be assembled in China, too? The U.S. does have automobile manufacturing expertise. And a car is so utterly unlike any product Apple has ever made that I feel like anything is possible. ↩︎

    -
  2. -
-
- - - - ]]>
- ★ Why Apple Assembles in China
- - - - tag:daringfireball.net,2015://1.31881 - 2015-12-11T21:19:40Z - 2015-12-15T00:38:58Z - - John Gruber - http://daringfireball.net/ - -Regarding Apple’s new Smart Battery Case for the iPhone 6/6S.

-]]>
- Joanna Stern tested Apple’s new Smart Battery Case for five days, and likes it a lot:

- -
-

Let’s get this out of the way: The bar for battery-case design is -extremely low. Most are chunky and made of black matte plastic, -requiring you to attach two pieces to your phone. You choose a -battery case for utility, not fashion.

- -

Apple’s Smart Battery Case, though still fairly unsightly, is -ahead of those. Bend back the top and slide in your phone. It -feels just like Apple’s smooth, soft-touch wraparound silicone -case, except… with a protruding, awkward battery on the back. The -battery juts out as if your phone will soon give birth to a -rectangular alien.

- -

Still, I’ll take it over all the ugly messes sold by Mophie, -Anker and others, especially since it provides better protection -for the phone. A lip curves just above the screen to prevent the -glass from hitting a hard surface and an interior lining provides -better shock absorption than hard plastic. Plus, the grippy -material is much easier to hold and doesn’t feel like it will -slip from my hands.

-
- -

The Verge’s Lauren Goode disagrees:

- -
-

Apple’s smart battery case is fine, then, if you want a softer -case or a “passive” battery charging experience, with zero control -over or understanding of how the case actually charges your phone. -Maybe that’s what Apple is hoping: that buyers of this thing will -slip it on and never take it off, charging their iPhones entirely -through the case’s Lightning port going forward, forgetting about -its big ol’ bump in the back. They will be pleased, finally, with -their iPhone 6’s or 6S’s battery life, and the memory of spending -an extra $99 for it, rather than having it just work that way in -the first place, will eventually fade away.

- -

It’s fine if you don’t want exterior indicator lights, or a even a -case that gives you a 0 to 100 percent charge. After all, this one -was designed for the iPhone, by the same company that made your -iPhone. For some people, that’s a big draw.

- -

In either case this will probably sell like hot cakes. It fits -nicely in holiday stockings. ’Tis the season. Just know that from -a pure performance and even a design perspective, Apple’s effort -is not the best you can get.

-
- -

(I can almost see her eyes rolling as she typed those italicized words in the second quoted paragraph.)

- -

Lewis Hilsenteger of Unbox Therapy best captured what most of us thought when we first saw it: “These things look weird.”

- -

That was certainly my first impression when I got mine Tuesday morning. The looks-like-it’s-pregnant-with-an-iPod-Touch design is certainly curious. I think to understand why it looks like this we have to ask why it even exists:

- -
    -
  • People who use their phones heavily — power users, if you will — struggle to get through a day on a single charge with the iPhone 6/6S.

  • -
  • The Plus models offer so much more battery life that getting through the day on a single charge isn’t a problem, even for power users who are on their phones all day long. But most people don’t want an iPhone that large.

  • -
  • Apple has long sold third-party battery cases in its stores, so they know how popular they are.

  • -
  • Existing battery cases all suffer from similar design problems, as outlined by Joanna Stern above. They make the entire device look and feel chunky, and most of them are built from materials that don’t feel good. None of them integrate in any way with the software on the iPhone, and most of them use micro USB instead of Lightning for charging the case.

  • -
  • Lastly, Apple claims the Smart Battery Case tackles a problem I wasn’t aware existed: that existing battery cases adversely affect cellular reception because they’re putting a battery between the phone’s antenna and the exterior of the case.

  • -
- -

So I think Apple’s priorities for the Smart Battery Case were as follows — and the order matters:

- -
    -
  1. Provides effective battery life equivalent to the iPhone 6S Plus.
  2. -
  3. Feels good in your hand.
  4. -
  5. Makes it easy and elegant to insert and remove the phone.
  6. -
  7. Works as a durable protective case.
  8. -
  9. Prevents the case’s battery from affecting cellular reception.
  10. -
  11. Looks good.
  12. -
- -

That “looks good” is last on the list is unusual for an Apple product, to say the least. Looking good isn’t always first on Apple’s list of priorities, but it’s seldom far from the top. But in this case it makes sense: Apple sells great-looking silicone and leather cases for people who aren’t looking for a battery case, and all existing third-party battery cases are clunky in some way.

- -

Ungainly though the case’s hump is, I can’t help but suspect one reason for it might be, counterintuitively, a certain vanity on the part of its designers. Not for the sake of the case itself, but for the iPhone. Third-party “thick from top to bottom” battery cases make it impossible to tell whether the enclosed phone is itself thick or thin. Apple’s Smart Battery Case makes it obvious that it’s a thin iPhone in a case which has a thick battery on the back. And I’ll say this for Apple: they are owning that hump. The hero photo of the case on the packaging is a face-on view of the back of the case.

- -

But I think the main reasons for this design are practical. The battery doesn’t extend to the top in order to accommodate the hinge design for inserting and removing the phone. Why it doesn’t extend to the bottom is a little less obvious. I suspect one reason is that that’s where the “passively coupling antenna” is.1 Extending the battery to cover it would defeat the purpose. Also, there’s a hand feel aspect to it — normally I rest the bottom of my iPhone on my pinky finger. With this case, I can rest the bottom ridge of the hump on my pinky, and it’s kind of nice. I also like putting my index finger atop the hump.

- -

So the Smart Battery Case looks weird. Typical battery cases look fat. Whether you prefer the weird look of the Smart Battery Case to the fat look of a typical case is subjective. Me, I don’t like the way any of them look. But after using the Smart Battery Case for three days, and having previously spent time using the thinnest available cases from Mophie, I feel confident saying Apple’s Smart Battery Case feels better when you’re holding it than any other battery case, both because of the material and its shape. It’s not even a close call. It also feels sturdier — this is the most protective iPhone case Apple has ever made, with rigid reinforced sides and a slightly higher lip rising above the touchscreen. The Smart Battery Case also clearly looks better from your own face-on perspective when using the phone. (Mophie’s cases look better than most, but they emboss an obnoxious “mophie” logotype on the front-facing chin. If Apple doesn’t print anything on the front face of the iPhone, why in the world would a case maker?)

- -

Patents, by the way, are a non-issue regarding the Smart Battery Case’s design. A well-placed little birdie who is perched in a position to know told me that Nilay Patel’s speculation that the unusual design was the byproduct of Apple trying to steer clear of patents held by Mophie (or any other company for that matter) are “absolute nonsense”. This birdie was unequivocal on the matter. Whether you like it, hate it, or are ambivalent about it, this is the battery case Apple wanted to make.

- -

My take is that the Smart Battery Case is an inelegant design, but it is solving a problem for which, to date, no one has created an elegant solution. Apple has simply chosen to make different severe trade-offs than the existing competition. In that sense, it is a very Apple-like product — like the hockey-puck mouse or the iMac G4.

- -

On Capacity, Simplicity, and the Intended Use Case

- -

Most battery cases have an on/off toggle switch, controlling when the case is actually charging the phone. The reason for this is that you can squeeze more from a battery case if you only charge the phone when it’s mostly depleted. Here’s a passage from Mophie’s FAQ page:

- -
-

When should I turn on my mophie case?

- -

To get the most charge out of your case, turn it on around 10%-20% -and keep the case charging without using it until your iPhone hits -80% battery life. From there, you can either wait until it gets -low again or top it off when the battery is less than 80%. Apple’s -batteries fast-charge to 80%, then switch to trickle charging for -the last 20%.

-
- -

Simplicity is a higher priority for Apple than fiddly control. If a peripheral can get by without an on/off switch, Apple is going to omit the switch. (Exhibit B: Apple Pencil.) The whole point of the Smart Battery Case is that you charge it up and put your iPhone in it and that’s it. Complaining about the lack of an on/off toggle or external charge capacity indicator lights on the Smart Battery Case reminds me of the complaints about the original iPhone omitting the then-ubiquitous green/red hardware buttons for starting and ending phone calls. Sure, there was a purpose to them, but in the end the simplification was worth it. If your iPhone is in the case, it’s charging. That’s it.

- -

Regarding the battery capacity of the case, here’s Lauren Goode, author of the aforelinked review for The Verge, on Twitter:

- -
-

A quick comparison for you: $99 Apple Battery Case 1877 mAh, -$100 Mophie Juice Pack Air 2750 mAh, $50 Incipio Offgrid Express -3000 mAh

-
- -

Nothing could better encapsulate the wrong way of looking at the Smart Battery Case than this tweet. The intended use of the Smart Battery Case is to allow prolonged, heavy use of an iPhone 6/6S throughout one day. In my testing, and judging by the reviews of others, its 1,877 mAh battery is enough for that. Adding a bigger battery would have just made it even heavier and more ungainly.

- -

And the very name of the Incipio Offgrid Express suggests that it is intended for an entirely different use case: traveling away from power for more than a day.

- -

Which in turn brings me to Tim Cook’s comments to Mashable’s Lance Ulanoff yesterday:

- -
-

Some also see the introduction of an Apple battery case as an -admission that battery life on the iPhone 6 and 6s isn’t all it -should be.

- -

Cook, though, said that “if you’re charging your phone every day, -you probably don’t need this at all. But if you’re out hiking and -you go on overnight trips… it’s kind of nice to have.”

-
- -

The Smart Battery Case would certainly help with an overnight hiking trip, but I think Cook was off-message here, because that scenario is really not what it was designed for. Big 5,000 mAh (or more) external battery chargers (or the highest capacity, extremely thick battery cases from third parties) are far better suited to that scenario than the Smart Battery Case. But Ulanoff’s preceding paragraph points to the marketing predicament inherent in a first-party Apple battery case: that it implies the built-in battery of the iPhone 6S is insufficient.

- -

The clear lesson is that it’s far better to give a phone more battery life by making the phone itself thicker and including a correspondingly thicker (and thus bigger) internal battery than by using any sort of external battery. After a few days using this case, my thoughts turn not to the Smart Battery Case itself but instead to my personal desire that Apple had made the 6/6S form factor slightly thicker. Not a lot thicker. Just a little — just enough to boost battery life around 15-20 percent or so.2 That wouldn’t completely alleviate the need for external batteries. But it would eliminate a lot of my need — my phone dies only a few times a year, but when it does, it almost invariably happens very late at night.

- -

I emphasized the word “personal” in the preceding paragraph because I realize my needs and desires are not representative of the majority. I think the battery life of the iPhone 6S as-is is sufficient for the vast majority of typical users. I suspect Cook went with the overnight hiking scenario specifically to avoid the implication that the built-in battery is insufficient. But the better explanation is that the built-in battery is insufficient for power users who use their iPhones far more than most people do.

- -

My Advice

- -

If you find yourself short on battery with your iPhone every day (or even most days), and you can’t make an adjustment to, say, put a charging dock on your desk or in your car to give your iPhone’s internal battery a periodic snack, then you should probably bite the bullet and switch to a 6S Plus. However bulky the Plus feels in your pocket and hands, it feels less bulky to me than the iPhone 6S with any battery pack. An iPhone 6S Plus, even with a normal case on it, weighs noticeably less than an iPhone 6S with the Smart Battery Case. If you need the extra battery capacity every day, you might as well get the Plus. (If you actually prefer the bigger Plus to the 4.7-inch devices, you’re in luck — you get the screen size you prefer, and a significantly longer-lasting battery. My advice here is for those who prefer the 4.7-inch size, other considerations aside.)

- -

That doesn’t describe me, however. On a typical day, my iPhone 6S seldom drops below 20 percent by the time I go to sleep. But when I’m traveling, I often need a portable battery of some sort. Cellular coverage can be spotty (which drains the battery), and when I’m away from home, I tend to do more (or even the entirety) of my daily computing on the iPhone. Conferences, in particular, can be dreadful on battery life. At WWDC my iPhone can drop to 50 percent by the time the keynote is over Monday morning.

- -

In recent years, rather than use a battery case, I’ve switched to carrying a portable external battery. My favorite for the past year or so is the $80 Mophie Powerstation Plus 2X. It’s relatively small, packs a 3,000 mAh capacity, and has built-in USB and Lightning cables. At conferences or for work travel, it’s easily stashed in my laptop bag, so my pockets aren’t weighed down at all, and my iPhone isn’t saddled with an unnatural case. If I do need to carry it in my pocket, it’s not too bad. It’s also easier to share with friends or family than a battery case. At night, I just plug the Powerstation into an AC adapter, and my iPhone into the Powerstation, and both devices get charged — no need for a separate charger or any additional cables.

- -

The big advantage to using a battery case instead of an external battery pack is that you can easily keep using your phone while it charges. That’s awkward, at best, while your phone is tethered by a cable to a small brick.

- -

If I were going to go back to using a battery case, there’s no question in my mind that I’d go with Apple’s. The only downside to it compared to Mophie’s (and the others — but I think Mophie is clearly the leader of the pack) is that it looks funny from the back. But to my eyes it doesn’t look that funny, and though third-party cases don’t look weird, they don’t look (or feel) good. In every other way, Apple’s Smart Battery Case wins: it’s all Lightning, so any Lightning peripherals you have will work, and there’s no need to pack a grody micro USB cable; it supplies more than enough additional power to get you through an active day; its unibody design makes it much easier to insert and remove the phone; and it feels much better in hand.

- -
-
-
    -
  1. -

    My understanding of how this “passively assistive antenna” works is that it takes the cellular signal and amplifies it as it passes through the case in a way that makes it easier for the iPhone’s antenna to “hear”. Sort of like the antenna equivalent of cupping your hand around your ear. I have no idea whether this is legit, or some sort of placebo marketing bullshit, but it would be interesting to see someone measure the cellular reception of (a) a naked iPhone 6S, (b) the same iPhone in a, say, Mophie battery case, and (c) the same iPhone in the Smart Battery Case. ↩︎

    -
  2. -
  3. -

    The iPhone 6 and 6S are actually 0.2mm thinner than their corresponding Plus models. That’s sort of crazy. The difference is barely perceptible, but if anything, the 6 and 6S should be a little thicker, not thinner, than the Plus models. ↩︎︎

    -
  4. -
-
- - - - ]]>
- ★ The Curious Case of the Curious Case
- - - - tag:daringfireball.net,2015://1.31795 - 2015-11-14T04:57:52Z - 2015-12-09T03:58:34Z - - John Gruber - http://daringfireball.net/ - -Take away every single iPhone sold — all of them — and Apple’s remaining business for the quarter was almost as big as Microsoft’s, bigger than Google’s, and more than four times the size of Facebook’s.

-]]>
- This piece by Bryan Clark for TheNextWeb caught my eye last weekend — “We’ve Reached — Maybe Passed — Peak Apple: Why the Narrative Needs to Change”:

- -
-

Last month, Apple’s latest earnings call announced its “most -successful year ever.” The numbers were reported, the stories -were spun and Wall Street basically anointed Apple the god of -capitalism.

- -

They’re all wrong.

-
- -

Apple wasn’t wrong — fiscal 2015 was Apple’s most successful year ever, by the objective measures of both revenue and profit. I suppose you can decide to define “most successful year ever” in terms of something else, like percentage growth or stock price gains, but revenue and profit are pretty fair measures.

- -

I missed it where “Wall Street basically anointed Apple the god of capitalism”. All I noticed was that Apple’s stock price went up about two percent the day after earnings were announced and has since fallen back to where it was before Q4 earnings were announced.

- -
-

The actual story, the story we should be telling, involves a -different narrative. Apple is the largest company in the world, -but success is fleeting. While the numbers are impressive, they -don’t come close to painting an accurate picture about how much -trouble Apple is really in.

- -

Apple’s rise under Steve Jobs was historic. Its fall under Tim -Cook is going to be much slower, more painful.

-
- -

The fall usually is more painful than the rise. Who writes a sentence like that?

- -

And if Apple’s fall under Cook is much slower than its rise under Steve Jobs, it’s going to take 20 or 30 years. Apple’s revival was long, slow, and relatively steady.

- -
-

Apple lives and dies by the iPhone. iPad sales are flat, -iPod’s are all but irrelevant, and while Mac sales are up, -they’re nowhere close to the workhorse that can continue to -carry Apple should they experience a downturn in iPhone sales. -There is no Plan B.

- -

One look at the numbers tells a pretty decisive tale.

- -

Percentage of revenue derived from iPhone sales:

- -
    -
  • 2012: 46.38%
  • -
  • 2013: 52.07%
  • -
  • 2014: 56.21%
  • -
  • 2015: 62.54%
  • -
-
- -

This is the part of Clark’s piece that got my attention. It’s a common refrain these days — just search Google for “Apple is too dependent on the iPhone”.

- -

Clark makes it sound like this is because the rest of Apple’s business is in decline, whereas the truth is that the iPhone continues to grow at an astonishing rate that even Apple’s other successful products can’t match. Is it worrisome that iPad sales continue to decline? Sure. Would it be better for Apple if the iPad were selling in iPhone-esque quantities? Of course. But iPad still sold 9.9 million units and generated $4.3 billion in revenue last quarter.

- -

Arguing that Apple is in trouble because the iPhone is so popular is like arguing that the ’90s-era Chicago Bulls were in trouble because Michael Jordan was so good. It’s true Jordan couldn’t play forever — and the iPhone won’t be the most profitable product in the world forever. But in the meantime, the Bulls were well-nigh unbeatable, and Apple, for now at least, is unfathomably profitable.1 Just like how it’s better to have loved and lost than never to have loved at all, it’s better to have tremendous success for some period of time than never to have had tremendous success in the first place. Right?

- -

What I don’t get is why Apple gets singled out for its singular success, but other companies don’t. 92 percent of Google’s revenue last year came from online advertising. And more importantly, I don’t get why Apple’s non-iPhone businesses are so quickly written off only because they’re so much smaller than the iPhone.

- -

Apple’s total revenue for last quarter was $51.5 billion. The iPhone accounted for $32.2 billion of that, which means Apple’s non-iPhone business generated about $19.3 billion in revenue. All of Microsoft in the same three months: around $21 billion. All of Google: $18.78 billion. Facebook: $4.5 billion. Take away every single iPhone sold — all of them — and Apple’s remaining business for the quarter was almost as big as Microsoft’s, bigger than Google’s, and more than four times the size of Facebook’s. And this is for the July-September quarter, not the October-December holiday quarter in which Apple is strongest.

- -

Nothing in the world compares to Apple’s iPhone business, including anything else Apple makes. But a multi-billion-per-quarter business here (Mac), a multi-billion-per-quarter business there (iPad), a “Services” division that generates more revenue than Facebook, and an “Other” category (Watch, Apple TV, Beats, iPod) that booked $3 billion in a non-holiday quarter — and it’s clear that Apple’s non-iPhone businesses, combined, amount to a massive enterprise.

- -

Here’s a Larry Dignon column about whether iPad Pro will make “iPad material to Apple again”:

- -
-

Apple’s iPad sales are on the borderline of being immaterial to -the company, but some analysts are betting that enterprise sales -of the iPad Pro can turn the product line around. […]

- -

Nevertheless, the iPad franchise is sucking wind relative to the -iPhone. Apple’s annual report shows the iPad is 10 percent of -overall sales. Once a business falls below 10 percent a company -doesn’t have to break it out. In other words, the iPad could be -lumped into “other” with the Apple Watch and iPod if current -trends continue.

-
- -

This is a product line that, in and of itself, generated just about exactly the same revenue last quarter as all of Google’s non-advertising business did for the entire fiscal year. But Apple is the company that is considered lopsided and worrisomely dependent upon a single product.

- -

Name a product introduced in the last five years that has been more successful than the iPad — either in terms of revenue and profit for its maker, or in terms of aggregate hours of daily use and customer satisfaction of its users. I can’t think of one.

- -

Now consider the Apple Watch. Fast Company called it “a flop” back in July. Here’s a guy on Quora — Jason Lancaster, editor of a website called Accurate Auto Advice — answering, in the affirmative, whether Apple has “already lost the market for self driving cars” (not joking):

- -
-

Third, Apple may have peaked. Call me a hater, but what reason is -there to assume Apple’s reputation is going to stay where it is? -The watch was a flop, and their only consistent source of success -is the iPhone, as the market for Macs and iPads is drying up (as -it is for all computer hardware companies).

-
- -

Forget the fact that Mac sales are growing, or that iPad sales, though in decline, remain roughly 10 million per quarter. What I enjoy about this is Lancaster’s having written off the Watch as a flop — he even uses the past tense.

- -

Here’s what that flop looks like:

- -
-

Apple has shipped seven million Apple Watches since its -introduction this spring, giving the technology giant a firm lead -in the nascent smartwatch market, according to researcher Canalys.

- -

That number falls shy of some Wall Street analysts’ expectations -for Apple’s first new device category since 2010. But, for -perspective, consider this: Apple sold more smartwatches from -April through September than all other vendors combined sold over -the past five quarters, Canalys reports.

-
- -

If we estimate the average selling price for an Apple Watch at $500 (reasonable), that’s $3.5 billion in revenue for the year to date — prior to the holiday quarter that is almost certainly going to be the strongest for watch sales annually.

- -
- -

Back to Bryan Clark’s TheNextWeb piece:

- -
-

Steve Jobs is almost entirely responsible for Apple’s cult-like -following.

- -

By streamlining the company in an attempt to make it profitable, -the same vision started to makes its way through every product -Apple created. Rather than bloated and flashy, Jobs created a -movement of decidedly minimalist devices that required not much -more than an occasional charge and a user that knew where the -power button was.

- -

Between aesthetically pleasing design, rock-solid hardware, and -software that responded as if it were built for the machine — -not in spite of it — Apple culture became a cult of -Jobs-worshipping consumers willing to buy anything with a -lowercase “i” in front of it.

-
- -

That never happened. The G4 Cube didn’t sell. iPod Hi-Fi didn’t sell. Those weren’t just non-hit products — they were both products that Steve Jobs himself really liked. I’ve heard that he had a stack of unopened iPod Hi-Fis in his office. Apple products have never been blindly accepted by the mass market — they’ve succeeded on their merits and by meeting actual demand. As I wrote two years ago:

- -
-

To posit that Apple customers are somehow different, that when -they feel screwed by Apple their response is to go back for more, -is “Cult of Mac” logic — the supposition that most Apple -customers are irrational zealots or trend followers who just -mindlessly buy anything with an Apple logo on it. The truth is the -opposite: Apple’s business is making customers happy, and keeping -them happy. They make products for discriminating people who have -higher standards and less tolerance for design flaws or -problems.

-
- -

Clark finally tells us what Apple’s biggest problems are:

- -
-

There are larger issues on the horizon: For example, how does -Apple compete with Windows and Android?

- -

Both have proven to be amazingly adept in recent years not only at -competing with Apple in form factor, but functionality as well.

- -

Two companies that are innovating, not searching for identity -outside of a singular product.

- -

Two companies that are on the way up, not down.

-
- -

Windows and Android, got it.

- -
-

The Apple Watch is great, but it’s never going to carry Apple like -the iPhone until it works like one. The watch is undeniably cool, -but it really fails to do anything better than your phone.

- -

To make matters worse, you have to have an iPhone close by in -order to even use most of its features. Similar Android models are -self-contained and only require an occasional sync.

- -

The autonomous car project sounds promising, but competing against -Google and Tesla in addition to auto industry giants like Lexus -and Mercedes is an uphill battle full of technology challenges, -government red tape and changing century-old transportation -conventions.

-
- -

The best I can gather from this mishmash of a conclusion is that Apple Watch should have somehow debuted as a first-generation product that could stand toe-to-toe with the iPhone (which is now in its ninth generation), and that Apple’s car product should already be here. If there were no rumors of an Apple car, we’d be hearing that Apple is going to miss out on the next big industry that is ripe for disruption from the tech industry. But because there are rumors and hints pointing to an Apple car, we’re hearing that cars are too difficult, the established companies too entrenched. Ed Colligan’s line for the ages — “PC guys are not going to just figure this out. They’re not going to just walk in.” — was also about an industry full of longstanding giants, Google, technology challenges, government red tape, and century-old conventions. Minus the “government red tape”, that’s a pretty good description of the watch and home entertainment system industries, too.

- -

I’m not here to argue the opposite of Colligan — that Apple’s success in these new fields is preordained — because that would be foolish. But it’s just as foolish to argue that Apple can’t succeed — or that anything less than iPhone-sized success in a new endeavor is a failure.

- -
-
-
    -
  1. -

    The iPhone, however, is unlikely to take a year off in the prime of its career to play baseball↩︎

    -
  2. -
-
- - - - ]]>
- ★ What Goes Up
- - - - tag:daringfireball.net,2015://1.31778 - 2015-11-11T13:08:58Z - 2015-11-13T08:05:24Z - - John Gruber - http://daringfireball.net/ - -The future of mass market portable computing involves neither a mouse pointer nor an x86 processor.

-]]>
- First impressions last a lifetime, goes the adage. You’re going to have to forget your first impressions of the iPad to understand the iPad Pro.

- -

When Apple introduced the original iPad in 2010, it was explicitly positioned in a new role for a device — somewhere between an iPhone and a MacBook. That seems obvious, but the problem, for the iPad, is that people loved their iPhones and MacBooks. The only way iPad would succeed, Steve Jobs said, was if it were “far better at doing some key things” than either an iPhone or MacBook.

- -

Apple succeeded. Simply by nature of having a bigger display, the iPad was better than the iPhone for numerous tasks — watching videos or reading long-form text, to name just two. No one would dispute that bigger displays are better for certain tasks — you can prove the productivity gains.

- -

What made the iPad better than a MacBook, in at least some ways, was more subjective than objective. Objectively, a MacBook was faster, by a large factor, could multitask, and offered a rich library of serious productivity apps. A Mac was, simply put, more powerful than an iPad — both in terms of hardware and software. The iPad had some objective advantages — battery life and the pixel density of its display are two that come to mind.1

- -

The trade-offs were obvious. The iPad offered the same conceptual simplicity and intimacy as the iPhone, with the “lean-back” ergonomics of a tablet, at the cost of power — hardware performance and software complexity.

- -

It was, in short, just a big iPhone. To the eyes of many in the tech industry, “just a big iPhone” was damning. They wanted the iPad to impress in terms of power. To the eyes of tens of millions of users, however, “just a big iPhone” was strong praise. An iPhone with a 10-inch display sounded just great.

- -

The intervening five years have turned all of this upside down. The iPad Pro now impresses solely by dint of its engineering. Anyone who doesn’t see this is blinded by their established impressions of the first few iPads.

- -

For the moment, put aside the form factor differences (tablet with optional keyboard vs. hinged clamshell), conceptual differences in iOS and OS X (direct touchscreen manipulation of full-screen apps vs. a mouse pointer and tiled windows) and software differences (simpler iOS apps vs. more complex OS X apps). All those points are worth consideration, but for now, put them aside. Right now, today, the iPad Pro is a peer to the current lineup of MacBooks in terms of computational hardware performance.

- -

The iPad Pro is without question faster than the new one-port MacBook or the latest MacBook Airs. I’ve looked at several of my favorite benchmarks — Geekbench 3, Mozilla’s Kraken, and Google’s Octane 2 — and the iPad Pro is a race car. It’s only a hair slower than my year-old 13-inch MacBook Pro in single-core measurements. Graphics-wise, testing with GFXBench, it blows my MacBook Pro away. A one-year-old maxed-out MacBook Pro, rivaled by an iPad in performance benchmarks. Just think about that. According to Geekbench’s online results, the iPad Pro is faster in single-core testing than Microsoft’s new Surface Pro 4 with a Core-i5 processor. The Core-i7 version of the Surface Pro 4 isn’t shipping until December — that model will almost certainly test faster than the iPad Pro. But that’s a $1599 machine with an Intel x86 CPU. The iPad Pro starts at $799 and runs an ARM CPU — Apple’s A9X. There is no more trade-off. You don’t have to choose between the performance of x86 and the battery life of ARM.

- -

We’ve now reached an inflection point. The new MacBook is slower, gets worse battery life, and even its cheapest configuration costs $200 more than the top-of-the-line iPad Pro. The iPad Pro is more powerful, cheaper, has a better display, and gets better battery life. It’s not a clear cut-and-dry win — MacBooks still have more RAM (the iPad Pro, in all configurations, has 4 GB of RAM, although Apple still isn’t publishing this information — MacBook Pros have either 8 or 16 GB), are expandable, and offer far more storage. But at a fundamental level — CPU speed, GPU speed, quality of the display, quality of the sound output, and overall responsiveness of interface — the iPad Pro is a better computer than a MacBook or MacBook Air, and a worthy rival to the far more expensive MacBook Pros.

- -

The entire x86 computer architecture is living on borrowed time. It’s a dead platform walking. The future belongs to ARM, and Apple’s A-series SoC’s are leading the way.

- -

The A9X didn’t come out of nowhere. Watching Apple’s A-series chips gain on x86 over the past five years, we’ve all been speculating about whether Apple might someday start using ARM chips in MacBooks. As of now, it’s only a question of whether they want to.

- -

What Apple Means by ‘Pro’

- -

With the Mac Pro, the “pro” really does stand for “professional”. There’s pretty much no reason for anyone to buy a Mac Pro unless their work is computationally expensive. There aren’t many people left whose work is slowed down regularly by the performance of their computer. The Mac Pro is aimed at that market. (That said, a higher-end iMac will outperform a Mac Pro in many tasks that aren’t well-suited to multicore parallel computing. The Mac Pro is due for an update.)

- -

With the MacBook Pro, on the other hand, “pro” isn’t really short for “professional”. It’s more like “deluxe” — a signifier that it’s a higher-end product than its non-pro siblings. Faster, better, and accordingly higher-priced. A MacBook Pro with 1 TB of SSD storage is indeed a terrific portable computer for “professional” use by, say, a photographer or film editor or software developer — people who truly stretch the performance of any computer today, portable or otherwise. But a decked-out MacBook Pro is also a terrific and perfectly reasonable choice for anyone who can simply afford one. MacBook Airs don’t have retina displays (and likely will never be upgraded to offer them), and the one-port MacBook is relatively slow.

- -

The iPad Pro is “pro” in the way MacBook Pros are. Genuine professionals with a professional need — visual artists in particular — are going to line up for them. But it’s also a perfectly reasonable choice for casual iPad users who just want a bigger display, louder (and now stereo) speakers, and faster performance.

- -

Anyone tying themselves in knots looking for a specific target audience for the iPad Pro is going about it the wrong way. There is no single target audience. Is the iPad Pro meant for office workers in the enterprise? Professional artists creating content? Casual users playing games, watching movies, and reading? The answer is simply “Yes”.

- -

Smart Keyboard and Converting to a Laptop Form Factor

- -

So unlike the original iPad of 2010, which carved out new territory between that of an iPhone and MacBook, the iPad Pro is clearly an alternative to a MacBook. I’m sure someone out there will carry both a MacBook (of any sort) and an iPad Pro while traveling, but I don’t really see the sense of that. The iPad Mini makes perfect sense as a travel companion to a MacBook. The iPad Air does too — especially for someone who watches a lot of video or prefers larger type while reading. But the iPad Pro stands as an alternative to a MacBook. If you want to carry a MacBook, you want a smaller, lighter iPad as a companion, and you don’t need a keyboard for it. If you want to carry an iPad Pro, you might as well get the Smart Keyboard cover and leave the MacBook at home.

- -

The trade-offs are varied. If you don’t type much, or don’t mind using the on-screen keyboard when you do, you’re probably already sold on the iPad-as-primary-portable-computer lifestyle. If you do type a lot and want a hardware keyboard, the appeal of the iPad Pro is going to largely hinge on your affinity for the Smart Keyboard.

- -

I’ve been using this iPad Pro review unit (128 GB, with cellular — top of the line kit, natch) for eight days, and most of that time I’ve had the Smart Keyboard attached. For just plain typing, it’s not that bad — I’ve written this entire review using it, Federico Viticci-style. I went into it thinking that my biggest complaint would be the keys themselves — I like my keyboards clicky, with a lot of travel. But I adjusted to it pretty quickly, and I kind of like the way it feels, as a tactile surface. It almost feels like canvas.

- -

My complaints and frustrations are more from the software, both iOS 9.1 itself and individual apps, both from Apple and third-party developers. Trying to use the iPad Pro as a laptop with the Smart Keyboard exposes the seams of an OS that was clearly designed for touchscreen use first. These seams aren’t new — I’m sure anyone who has tried using an iPad of any sort with a paired Bluetooth keyboard has run into the same things. This is simply the first time I’ve tried using an iPad with a hardware keyboard for an extended period for large amounts of work.

- -

I almost wrote “for large amounts of writing” in the preceding paragraph, but the problems with an iPad and a hardware keyboard are more than about typing. A large part of my work is reading, and with a laptop, the keyboard is a big part of the reading experience. In fact, with the iPad Pro, the keyboard is even more important than it is on a MacBook — and today, it falls short.

- -

Here’s what I mean. First, when the iPad Pro is open with the keyboard attached, holding your arm up to touch the screen for anything longer than a moment or two is ergonomically uncomfortable. Apple has stated for years that this is why they don’t make the displays on MacBooks or iMacs touchscreens (that, combined with the relatively tiny click targets of Mac OS X, which are designed for very precise mice and trackpads, not imprecise finger tips). Scrolling through a long document using the iPad Pro touch screen is uncomfortable when it’s in laptop position. Going through a slew of new emails, likewise. In laptop mode, I want to use the keyboard for these things — and in most cases, because of bugs and/or software limitations, I can’t. That the keyboard falls short in these cases is even worse on iPad than it would be on a MacBook, because a MacBook has a trackpad. The point is, if my fingers are on the keyboard, I don’t want to move my hands. With a trackpad, I don’t have to. With the iPad Pro, I do.

- -

It’s an ancient (meaning dating back to the Classic era) Mac convention that in a read-only scrolling view, you can use the space bar to page down. When your eyes get to the bottom of the display, you can just hit space and the view should scroll to show the next screen full of content — with the last line or two of the previous screen now repeated at the top of the new screen to provide context as your eyes move from the bottom to the top of the display. This works almost everywhere on OS X, and anywhere it doesn’t work should be considered a bug.

- -

On iOS 9.1, Safari tries to support this, but it is dreadfully buggy. Instead of paging down just less than one screen-height of content, it pages down about 1.5 screen-heights of content. It literally scrolls right past huge amounts of content, rendering the feature completely unusable.

- -

Here’s a sample page I’ve created to illustrate. It’s just a simple text file with 1,000 lines, numbered in order. When I view that on my MacBook Pro, I see lines 1–45 (and half of line 46). When I hit space to page, the view scrolls and I now see lines 44–89. Hit space again and the view scrolls to show lines 88–132.

- -

On iPad Pro, I see lines 1–49 initially. But when I hit space to page down, the view scrolls to show me lines 75–123. Lines 50–74 are completely skipped past. It’s not even just a line or two — it’s huge chunks of text. This happens in all web pages in Safari on iOS 9.1, and it is not specific to the iPad Pro and Smart Keyboard. I see the exact same behavior on any iPad with a paired Bluetooth keyboard.

- -

Mail is another app in which, on my Macs, I depend heavily on the keyboard for scrolling and selection. On iPad, Mail does let you move from message to message using the keyboard (⌘↓ and ⌘↑), but it doesn’t support scrolling the actual message content — the space bar does nothing, and the Smart Keyboard doesn’t have a proper Page Down key.

- -

The space bar doesn’t work as a Play/Pause toggle for audio or video, either. I think it should.

- -

I don’t think it’s inherently problematic that iOS has no conceptual support for a mouse pointer, and thus can’t work with any sort of trackpad. But, given this constraint, good support for navigating as much of the UI as possible using the keyboard is more important on the iPad than it is on the Mac. But iOS’s support for navigating using the keyboard is worse.

- -

Another problem: when editing a long document, if you use the arrow keys to move the insertion point above the first line on screen or below the last line on screen, the insertion point just disappears off screen. The view doesn’t scroll to keep the insertion point visible, which is clearly what should happen (and does happen on OS X). Surely iOS will work this way eventually, but right now it still shows its roots as a touchscreen OS where a hardware keyboard is a decided second-class citizen.

- -

All is not lost, however. ⌘-Tab works for app switching just like it does on the Mac. Tap it and release and you switch to the most-recently used app. Tap it and keep holding down ⌘ and you get a visual switcher showing the 10 most-recently-used apps. (Again, this works with any hardware keyboard connected to any iPad — it’s just that this has been the first time it’s been relevant to me, personally.) The Smart Keyboard lacks a Home button, but there is a system-wide shortcut that maps ⌘-Shift-H to “Home”. Not bad, but once you’re at the iOS home screen, there’s not much you can do without touching the screen. For a few days, I sort of wished that I could use the arrow keys to navigate the home screen, with the selected app icon popping “up” like in the “focus” UI of the new Apple TV. But that idea, I suspect, is too far afield from the regular touchscreen-based UI of the iOS home screen. My keyboard idea requires a select-then-act two-stage model — the regular touch-based launcher is single-stage: just tap.

- -

But then I realized that the problem I wanted to solve wasn’t that I wanted the home screen to be keyboard-navigable. The problem was that I wanted to use the keyboard to launch apps that weren’t listed in the ⌘-Tab switcher. To do that on iOS without a hardware keyboard, you go home, then tap the app. With a keyboard, though, you can do it, just in a different way.

- -

Hit ⌘-Space system wide, and you’ll be taken to the home screen’s system-wide “Quick Search”. It’s like the iOS equivalent of Spotlight. Start typing the name of the app you want to launch, and there it is.

- -

But go ahead and play a sad trombone wah-wah here, because at this point, you still have to pick your arm up and touch the screen to launch the app. You can also use Quick Search for starting a web search in Safari, or anything else. But you can’t use the keyboard arrow keys to navigate the list of results. (Another problem with Quick Search using the keyboard: you have to wait a second or so for the Quick Search text field to accept input. I’m pretty sure it’s because we’re waiting for the animation to complete — first to show the home screen, then to jump to Quick Search. So if you type ⌘-Space and immediately begin typing what you’re looking for, the first few characters you type are lost. The user should never have to wait for the computer, especially if it’s just for an animation. Any Mac user with muscle memory trained by LaunchBar, Alfred, Quicksilver, or even Spotlight is going to find this enforced delay on iOS maddening.)

- -

This lack of keyboard support is prevalent system-wide. In Messages, if you start a new conversation and type the partial name of a contact, you can’t select from the list of matches using arrow keys or auto-complete the name you’ve partially typed using Tab. You’ve got to — you guessed it — reach up and touch the screen. You can use the arrow keys to select from a list of suggestions in the recipients fields in Mail, however, and arrow keys also work for selecting from the list of suggestions in the Safari location field.

- -

The bottom line is that the potential of the iPad Pro as a laptop is tremendous. The keyboard is just fine for typing, and the magnetic connection between the iPad Pro and the keyboard is surprisingly sturdy. You can absolutely use it as a literal laptop without any worry that the iPad Pro is going to fall off the Smart Keyboard. I even like the 4:3 aspect ratio — it shows more lines of text when reading than my 13-inch MacBook Pro. It also occupies a smaller footprint than an open MacBook Pro, meaning it should fit better on the seatback tray of an airplane. But the lack of pervasive support for keyboard-based UI navigation in iOS is a problem for anyone with longstanding Mac keyboard shortcuts ingrained in their muscle memory.

- -

As an actual cover, the Smart Keyboard does feel thick, and when closed, it bothers me a little that it’s thicker on the outer two thirds (where the keyboard is folded under) than the inner third. I wouldn’t recommend the Smart Keyboard for anyone who doesn’t plan to actually use the keyboard quite a bit. But if you do plan on using the keyboard frequently, the trade-off in thickness (compared to the non-keyboard Smart Cover) is well worth it.

- -

(It occurs to me that for many people, the Smart Keyboard might best be thought of not as a thick cover, but as a thin very portable desktop docking station.)

- -

Keyboard Bugs

- -

I experienced some flakiness with the keyboard throughout the week. Sometimes, system-wide keyboard shortcuts would stop working: ⌘-Tab, ⌘-Space, and ⌘-Shift-H. Typing within apps still worked, and keyboard shortcuts within any given app still worked, but the system-wide shortcuts inexplicably stopped working.

- -

Less frequently, I’ve seen the opposite problem: the system-wide keyboard shortcuts work, but keyboard shortcuts within any given app stop working. (iOS 9 has a very clever feature, by the way: press and hold the ⌘ key and you’ll see a HUD pop-up displaying all keyboard shortcuts available in the current context. This makes keyboard shortcuts more discoverable than they are on the Mac, where they’re spread across multiple menus in the menu bar.)

- -

In either case, I’ve been able to fix these keyboard problems by detaching and re-attaching the iPad from the Smart Keyboard. I don’t know if it’s a bug in iOS 9.1 or a faulty Smart Keyboard. (Apple has shipped me a second Smart Keyboard to test, but it won’t arrive until later in the day, after this review has been published. I’ll update it after the replacement arrives.)

- -

Apple Pencil

- -

It’s about precision: accuracy where you touch (Apple claims sub-pixel precision on screen), accuracy regarding pressure, and low latency regarding what you see on screen. I am not an illustrator, but I do know my own signature. My signature never looks like my actual signature when I have to sign electronically on a point-of-sale terminal. Usually it doesn’t even look close. On iPad Pro with Apple Pencil, it looks exactly like my signature when I sign with paper and ink. My handwriting looks like my handwriting, period (for better or for worse).

- -

All previous iOS devices have touchscreens designed for input from one source: fingertips. Fingertips are relatively fat and capacitive. The relatively fat size and imprecise location of a finger on screen is why tap targets are relatively larger and more spaced apart on iOS than OS X. This is also why third-party styluses for iOS devices have broad tips made of capacitive rubber — they’re more or less fake fingertips. The capacitive touchscreens on iPhones and (non-Pro) iPads aren’t designed for “fine tips”.

- -

Apple has done a few things regarding sampling the screen for input with Apple Pencil. First, there is something new in the display itself — something in the layer between the glass surface and the LCD display, I think. Or perhaps it’s under the LCD? Apple alludes to it in the Jony Ive-narrated video on the Apple Pencil web page, but they’re not really talking about it in detail.

- -

For capacitive (finger) touch, the iPad Pro samples at twice the rate of previous iPads — 120 times per second instead of 60. With the Pencil, though, the iPad Pro samples 240 times per second. The way the Pencil works requires cooperation with the display, and so there’s no way this Pencil could be made to work with existing iPads. The Pencil is not iPad Pro-exclusive out of product marketing spite — it’s exclusive to the Pro because the two were engineered in coordination with each other. And if Apple had designed the Pencil differently, to allow it to work with existing iPads, there’s no way it could have had this level of accuracy, because the tip would have needed to be broader and capacitive. (The Pencil’s tip is not capacitive at all — it doesn’t register as a touch at all on any other iOS device.)

- -

My guess is we’ll start to see Pencil support in future iOS devices in addition to the iPad Pro, starting with the iPad Air 3.

- -

Because the Pencil is round-barreled and has no clip on the cap, I was worried that it would roll around (and eventually, off) a table top. But it’s actually weighted inside, sort of like a Weeble Wobble, so unless it’s on a sloped surface, it won’t roll more than an inch or so before settling in place. In hand, I can’t tell that it’s weighted like this.

- -

I think most people who buy an iPad Pro are going to want a Smart Keyboard. The Apple Pencil is the more technically remarkable peripheral, but I suspect it’ll prove useful to far fewer people. Sketching apps like 53’s Paper and Apple’s own built-in Notes app certainly have appeal and utility to people who aren’t artists, but I suspect a lot of Apple Pencils are going to be bought out of curiosity and then go largely unused.

- -

For actual illustrators and artists, however, the Pencil and iPad Pro seem poised to be a career/industry-changing combination. What has been largely abstract — drawing using a device over here, looking at the results on a screen over there — can now be direct.

- -

Miscellaneous

- -
    -
  • Weight: The iPad Pro certainly feels heavier than recent iPads, but only in a way that’s commensurate with its increased size. It’s not too heavy.

  • -
  • Audio: The speakers are surprisingly loud. Apple told me the iPad Pro produces three times the audio volume of the iPad Air, and that certainly matches my experience. If you use your iPad as a small TV, the audio improvements might be more meaningful than the bigger display. The four-speaker stereo system is also very clever — no matter which way you rotate the iPad Pro, the top two speakers are for treble and the bottom two for bass.

  • -
  • Snap: Speaking of audio, if there’s a downside to the snug connection between the iPad Pro and the Smart Keyboard, it’s that the magnetic connection makes a rather loud snap when you connect or disconnect it. I can imagine some scenarios — in bed with a sleeping spouse, say — where this might be a problem.

  • -
  • Size classes: I think even Apple’s own apps are still figuring out how best to arrange layouts on this larger display. For example, in Mail, when the iPad Pro is in portrait, it only shows one column at a time. I think there’s clearly enough room horizontally, even in portrait, for a two-pane layout (narrow list of messages on left, wide message detail on right). The iPad Pro in portrait is as wide as the iPad Air in landscape — and the iPad Air in landscape uses two panes for Mail. Third-party developers are going to want to adjust their apps after they get a feel for what it’s like to use the iPad Pro for real.

  • -
  • Battery life: Simply outstanding. I didn’t even plug it in once between Monday and Friday, and it still had plenty of charge left. I’ve been using it for eight continuous hours as I type this sentence, and it still has more than a 50 percent charge remaining.

  • -
  • Missing apps: It’s been like this ever since the original iPad, but it still strikes me as odd that the iPad version of iOS lacks the Calculator, Weather, and Stocks apps. The Mac doesn’t have “apps” for Weather or Stocks, but it does have widgets for them in Notification Center. And it seems downright crazy for a computer not to have a built-in means for doing arithmetic. (Although you can do some arithmetic using Quick Search.)

  • -
  • Touch, Don’t Touch: For the past week I’ve really only used two computers. The iMac on my desk, and this iPad Pro. Today, though, I used my MacBook Pro while the iPad Pro was running benchmarks. And within a few minutes, I did something I have never once done before: I reached up and tried to touch something on the display. Ten minutes later I did it again. I point this out not to argue that I think MacBooks should have touch screens, but simply as an observation that even a lifelong Mac user can quickly get accustomed to the iPad Pro as a laptop.

  • -
- -

Conclusion

- -

From a hardware perspective, the iPad Pro strikes me as a seminal device. It runs faster than the Intel x86-based MacBooks, gets better battery life, and costs significantly less. And it has a better display with significantly more pixels than even a 15-inch MacBook Pro.

- -

Software-wise, support for the Smart Keyboard needs to get even smarter — but I’d be shocked if it doesn’t. For me, the iPad Pro marks the turning point where iPads are no longer merely lightweight (both physically and conceptually) alternatives to MacBooks for use in simple scenarios, to where MacBooks will now start being seen as heavyweight alternatives to iPads for complex scenarios.2

- -

Is it a MacBook replacement for me, personally? No. For you? Maybe. For many people? Yes.

- -

It brings me no joy to observe this, but the future of mass market portable computing involves neither a mouse pointer nor an x86 processor.

- -
-
-
    -
  1. -

    It’s kind of funny to think of a 2010 iPad with its 133 PPI display as “high resolution” — such a display looks comically fuzzy by today’s standards. But at the time it was a noticeably sharper display than what was in the MacBooks of the day — a 2009 13-inch MacBook Pro had a display with 113 PPI resolution↩︎

    -
  2. -
  3. -

    iOS 9’s split-screen multitasking really shines on the iPad Pro. I’ve found it useful on my iPad Air, but it’s downright natural on the iPad Pro. ↩︎︎

    -
  4. - -
-
- - - - ]]>
- ★ The iPad Pro
diff --git a/Frameworks/RSParser/RSParserTests/Resources/EMarley.rss b/Frameworks/RSParser/RSParserTests/Resources/EMarley.rss deleted file mode 100755 index 7c833eeef..000000000 --- a/Frameworks/RSParser/RSParserTests/Resources/EMarley.rss +++ /dev/null @@ -1,97 +0,0 @@ - - - <![CDATA[Stories by Liz Marley on Medium]]> - - https://medium.com/@emarley?source=rss-b4981c59ffa5------2 - - https://d262ilb51hltx0.cloudfront.net/fit/c/150/150/0*I9s5OlzJw_En0NzC.jpg - Stories by Liz Marley on Medium - https://medium.com/@emarley?source=rss-b4981c59ffa5------2 - - Medium - Sun, 28 Aug 2016 17:27:51 GMT - - - - - <![CDATA[UI Automation & screenshots]]> -

Here’s a partial collection of links from my talk today…

]]>
- https://medium.com/@emarley/ui-automation-screenshots-c44a41af38d1?source=rss-b4981c59ffa5------2 - https://medium.com/p/c44a41af38d1 - - Sat, 07 May 2016 23:53:30 GMT -
- - <![CDATA[They didn’t.]]> -

“The [software developer tool] team clearly doesn’t use [that tool] themselves.”

]]>
- https://medium.com/@emarley/they-didn-t-3a4dab489f45?source=rss-b4981c59ffa5------2 - https://medium.com/p/3a4dab489f45 - - Sat, 09 Jan 2016 15:29:25 GMT -
- - <![CDATA[Side quest: Drawing]]> -

]]>
- https://medium.com/@emarley/side-quest-drawing-b959ded1a1a4?source=rss-b4981c59ffa5------2 - https://medium.com/p/b959ded1a1a4 - - Wed, 09 Dec 2015 03:37:35 GMT -
- - <![CDATA[And if I somehow lose the iPad Pro, I can find that with Find My iPhone.]]> - ]]> - https://medium.com/@emarley/and-if-i-somehow-lose-the-ipad-pro-i-can-find-that-with-find-my-iphone-e9aa43486521?source=rss-b4981c59ffa5------2 - https://medium.com/p/e9aa43486521 - - Mon, 23 Nov 2015 19:38:20 GMT - - - <![CDATA[Though not as much more weight as you might expect.]]> - ]]> - https://medium.com/@emarley/though-not-as-much-more-weight-as-you-might-expect-7b33fe989f6e?source=rss-b4981c59ffa5------2 - https://medium.com/p/7b33fe989f6e - - Mon, 23 Nov 2015 19:37:38 GMT - - - <![CDATA[I avoided art classes in high school and college because I was afraid they would hurt my GPA.]]> - ]]> - https://medium.com/@emarley/i-avoided-art-classes-in-high-school-and-college-because-i-was-afraid-they-would-hurt-my-gpa-ab916601f2ad?source=rss-b4981c59ffa5------2 - https://medium.com/p/ab916601f2ad - - Mon, 23 Nov 2015 19:37:13 GMT - - - <![CDATA[Finding Value]]> -

I lose things a lot. Sometimes they’re just misplaced, sometimes gone forever. I don’t know if I have ever run out of ink in a pen—there’s…

]]>
- https://medium.com/@emarley/finding-value-20a90bf5ebf?source=rss-b4981c59ffa5------2 - https://medium.com/p/20a90bf5ebf - - Mon, 23 Nov 2015 19:34:18 GMT -
- - <![CDATA[Replaying this post in my head last night, I regret this word.]]> -

Keyboard shortcuts, and other little details may be programmatically simple to set up, but they are still an important part of an app’s…

]]>
- https://medium.com/@emarley/replaying-this-post-in-my-head-last-night-i-regret-this-word-d8ed0b43f0f9?source=rss-b4981c59ffa5------2 - https://medium.com/p/d8ed0b43f0f9 - - Tue, 10 Nov 2015 18:08:19 GMT -
- - <![CDATA[Betterment]]> -

I moved from Senior Test Pilot to Software Engineer last month.

]]>
- https://medium.com/@emarley/betterment-e0ef45fcd284?source=rss-b4981c59ffa5------2 - https://medium.com/p/e0ef45fcd284 - - Tue, 10 Nov 2015 02:17:46 GMT -
- - <![CDATA[This is a test.]]> -

This is only a test.

]]>
- https://medium.com/@emarley/this-is-a-test-6ab141a1c5b5?source=rss-b4981c59ffa5------2 - https://medium.com/p/6ab141a1c5b5 - - Sun, 20 Sep 2015 07:00:44 GMT -
-
-
\ No newline at end of file diff --git a/Frameworks/RSParser/RSParserTests/Resources/KatieFloyd.rss b/Frameworks/RSParser/RSParserTests/Resources/KatieFloyd.rss deleted file mode 100755 index c8b9d501e..000000000 --- a/Frameworks/RSParser/RSParserTests/Resources/KatieFloyd.rss +++ /dev/null @@ -1,352 +0,0 @@ - - - - - - - en - katiefloyd - - - - http://www.katiefloyd.com - <![CDATA[Katie Floyd]]> - https://static.feedpress.it/logo/katiefloyd.png - - Katie Floyd - http://www.katiefloyd.com - Tue, 23 Aug 2016 21:32:07 +0000 - en-US - Site-Server v6.0.0-8715-8715 (http://www.squarespace.com) - - - Special Mac Power Users for Relay FM Members - Katie Floyd - Thu, 25 Aug 2016 22:20:00 +0000 - http://tracking.feedpress.it/link/980/4243452 - 50c628b3e4b07b56461546c5:50c658a6e4b0cc9aa9ce4405:57bcbe83e4fcb567fdffc020 - - - - - - - - - - -

Relay FM has created a membership system to allow listeners to support the hosts of their favorite Relay FM shows. Listeners can choose to support a specific show like Mac Power Users, or support all the shows on the network. One of the perks of membership is that members will receive a bonus episode shows during Relay's anniversary week in August, including a very special bonus episode of Mac Power users.

Since this show didn't have to conform to a typical format, David and I knew instantly what we wanted to talk about. Star Trek and Star Wars. We spent almost an hour talking about our love for the series, how we became fans, our favorite characters and movies, what we think about the reboots and some of our traditions. It was a lot of fun.

If you're a Relay FM member, you can grab your special bonus episode of Mac Power Users now, along with special episodes of many of the other shows on Relay. If you're not a member, you can sign up now and gain access to all the members-only content.

My sincere thanks to all the members for your kind support of David and me and all the wonderful hosts on Relay FM!

]]>
-
- - Mac Power Users #336: iPhone Productivity - Relay FM - Katie Floyd - Tue, 23 Aug 2016 21:19:22 +0000 - http://tracking.feedpress.it/link/980/4224355 - 50c628b3e4b07b56461546c5:50c658a6e4b0cc9aa9ce4405:57bcbe39b8a79b49057ca264 - This week on Mac Power Users David and I share some of the most efficient productivity tasks and apps appropriate for the iPhone. Topics include calendars and contacts, email, task management, scanning, writing, and additional utilities for productivity.

This episode of Mac Power Users is sponsored by:

  • Casper: Because everyone deserves a great night sleep. Get $50 off with the code ‘MPU’
  • Eero: Blanket your home in fast, reliable WiFi.
  • The Omni Group We're passionate about productivity for Mac, iPhone and iPad. 
  • Automatic The connected car company that improves your driving and integrates your car into yoru digital life. Enter code MACPOWER to get 20% off your purchase.
]]>
-
- - Katie's Week In Review: August 21, 2016 - Katie Floyd - Sun, 21 Aug 2016 20:23:47 +0000 - http://tracking.feedpress.it/link/980/4204283 - 50c628b3e4b07b56461546c5:50c658a6e4b0cc9aa9ce4405:57ba0da8b8a79bd395ce38f9 - It’s a crazy weekend for me so I’m leaving you with a more abbreviated “link list” for the week in review post this week. Nevertheless, here are a few links of note for the week ending August 21, 2016:

  • Apple Announces Environmental Progress in China & Applauds Supplier Commitment to Clean Energy - Apple continuing their commitment to the environment, this time by putting pressure on their suppliers in China to commit to clean energy. Apple suppler Lens Technology has agreed to run its Apple operations on entirely renewable energy.
  • Tim Cook, the interview: Running Apple ‘is sort of a lonely job’ - I enjoyed this interview by Jena McGregor of The Washington Post discussing his first five years as CEO after Steve Jobs. Cook talks about some of the missteps as CEO, how the job can be lonely at times and living up to expectations.
  • Apple, Google, FCC Team up to Stop Spam Robocalls Jeff Gamet of The Mac Observer explains how some of the biggest players in the telecom industry are finally stepping up to address the problem with spam phone calls. I received one earlier today. It’s gotten to the point where I don’t answer my phone (and sometimes miss important calls) if I don’t recognize the number. There’s a new feature in iOS 10 that will allow Apple to query a database to attempt to determine if a call is likely spam, but so far Apple hasn’t gone so far as to build or maintain one itself.
  • New Ways to Control Your Experience on Twitter - Twitter has finally taken some positive steps to allow people to attempt to filter out trolls and limit the notifications they receive from. Twitter says this is just the start of their steps to take back control of the platform and control harassment. We’ll see. Right now it seems that these features are only available in the native Twitter apps, which I don’t use because they also come with tradeoffs. Hopefully Twitter will open these features up to third party developers.
  • Introducing Automatic Pro - Automatic rolled out a new version of their connected car adapter this week. The highlights include a new 3G connection included in the purchase price so it doesn’t have to always be connected to bluetooth. This is great because I find my iPhone’s bluetooth connection can be flakey sometimes causing my Automatic to drop connection. I’ll be picking up one of the new adapters. (Full disclosure, Automatic sponsors Mac Power Users - you can save $20 using this link.)
  • AT&T Introduces new MobileShare Plans - Following a similar move by Verizon, in advance of the new iPhone announcement AT&T has announced new mobile data plans. The plans are more expensive (of course) but in lieu of overage charges you’ll have the option to throttle your speeds down to 128kbps when your exceed your data cap. While painful, it might be a nice alternative if you only have a short time left before your billing cycle resets.
  • New Star Trek postage stamps are being released to celebrate the 50th anniversary. They’re available now for pre-order through the USPS.
]]>
-
- - Mac Power Users #335: Workflows with John Voorhees - Katie Floyd - Tue, 16 Aug 2016 15:03:11 +0000 - http://tracking.feedpress.it/link/980/4160390 - 50c628b3e4b07b56461546c5:50c658a6e4b0cc9aa9ce4405:57b32b40be659468fb6d1031 - This week on Mac Power Users, David and I chat with John Voorhees. John is an attorney by day but also develops software and writes for Mac Stories. We chat with John about how he juggles all his tech related tasks while managing a busy day job. (We sympathize.) 

This episode of Mac Power Users is sponsored by:

  • PDFpen from Smile With powerful PDF editing tools, available for Mac, iPad, and iPhone, PDFpen from Smile makes you a Mac Power User.
  • 1Password Have you ever forgotten a password? Now you don't have to worry about that anymore.
  • Fujitsu ScanSnap ScanSnap Helps You Live a More Productive, Efficient, Paperless Life. 
  • Squarespace: Enter offer code MPU at checkout to get 10% off your first purchase.
]]>
-
- - Katie's Week in Review: August 14, 2016 - Katie Floyd - Sun, 14 Aug 2016 21:11:27 +0000 - http://tracking.feedpress.it/link/980/4146486 - 50c628b3e4b07b56461546c5:50c658a6e4b0cc9aa9ce4405:57b0de6be58c62414be1f6ab - Mark Gurman is back with new rumors about the iPhone and MacBook Pros. That and other exciting links of note for the week ending August 14, 2016:

  • Mark Gurman, who is known for his accuracy recently in reporting Apple rumors, claims to have details of the new iPhones. Writing for his new publication, Bloomberg, Gurman claims that the standout feature will be a dual-camera system on the larger iPhone, a re-designed home button that appears to be touch sensitive rather than a physical click wheel and removal of the headphone jack. Gurman says we can also expect a hardware design similar to that of the past two years, perhaps making way for a more radical redesign next year for the 10-year anniversary of the iPhone. Gurman’s report, if true, seems to confirm previously reported rumors but doesn’t seem to bring a whole lot of additional information to the table.
  • Also this week at Bloomberg, Gurman claims Apple is planning its first major Pro laptop overhaul in four years. Again, Gurman’s report seems to give credit to previously reported rumors claiming the notebooks will be thinner, include a touch screen strip for function keys and feature more powerful processors along with USB-C technology along with a Touch ID sensor. However, Gurman says the MacBook Pros likely won’t debut alongside the new iPhone at an event rumored for September 7th. I’ve been hanging on to my 13" MacBook Air waiting for Apple to release new notebooks and if most of this is true, it sounds like just the update I’ve been waiting for.
  • This week Fast Company ran a cover story promoting an “inside look at Tim Cook’s Apple” It features an interview with Eddie Cue and Craig Federighi with follow-up by TimCook along with a sidebar featuring Bozoma Saint John about Apple Music.
  • It’s about time. Google has finally updated Google Docs, Sheets and Slides for iOS with split view support. Jason Snell of Six Colors has the details. Like Jason, David and I use Google Docs and Sheets to plan and prep Mac Power Users and the inability to use these apps in split screen on iOS was problematic. It only took them a year - but I guess better late than never.
  • Google has also announced that the Chrome browser will begin blocking Flash that runs in the background of webpages and make HTML5 the default beginning in September. John Voorhees of MacStories reports that Chrome 55 will make HTML5 the default except for sites which only support flash. I don’t load Flash on any of my computers but find myself occasionally using Chrome (which currently has Flash support built-in) to load a site that requires it. I’m unclear how that might change in the future, but it sounds like Chrome will still support Flash in the future, just not as a default.
  • IFTTT has released a new feature that allow recipes to live inside other apps. This will allow users to browse and activate select IFTTT recipes from within the developer’s app. Currently integrations are live with partners including LIFX, Garageio and Skybell with additional integrations coming online soon from partners including Automatic and Ring.
  • Last week I mentioned I was interested in the Logitech Create 9.7" iPad Pro keyboard case and I might have considered it if I hadn’t just bought the Apple Smart Keyboard for my iPad. This week Phil Dzikly of iLounge posted a review of the Crate 9.7". Overall it earned a high recommendation from the iLounge editors, though it appears to be a bit bulkier than Apple’s solution.
  • My thanks to MailButler for sponsoring the site this week. If you’re interested in having your product or service featured on KatieFloyd.com, consider an RSS sponsorship, details can be found here.
  • Finally, this week the official trailer for Rogue One: A Star Wars Story was released. I have to admit, it gave me goosebumps. The trailer runs about 2 minutes. I can’t wait for the 2 hour Incomparable episode about it.
]]>
-
- - Mac Power Users #334: MPU Live: "She Can't Take It Anymore, Captain!" - Katie Floyd - Tue, 09 Aug 2016 23:40:00 +0000 - http://tracking.feedpress.it/link/980/4107789 - 50c628b3e4b07b56461546c5:50c658a6e4b0cc9aa9ce4405:57a7d4e9e4fcb59aef039cba - This week's Mac Power Users was a live show. David and I are joined by our pal Victor Cajiao who discusses all the steps of producing a modern music album. We also take listener questions and help troubleshoot an accidentally reformatted hard drive, discuss password schemes, using a ScanSnap for photos, share listener feedback on our Special Event and Keynote shows, discuss options for Evernote and share listener tips.

This episode of Mac Power Users is sponsored by:

  • 1Password Have you ever forgotten a password? Now you don't have to worry about that anymore. 
  • Gazelle Sell your iPhone for cash at Gazelle! 
  • Marketcircle We help small business grow with great Mac, iPhone and iPad apps including Daylight and Billings Pro.
  • Sanebox Stop drowning in email!
]]>
-
- - Sponsor: Inbox Zero In No Time With MailButler For Apple Mail - Katie Floyd - Mon, 08 Aug 2016 12:00:00 +0000 - http://tracking.feedpress.it/link/980/4094116 - 50c628b3e4b07b56461546c5:50c658a6e4b0cc9aa9ce4405:57a75d06ebbd1acee61ea0b0 - - - - - - - - - - - - - - - -

Every email user is familiar with this problem: important incoming emails often get lost, if they are not responded right away. They drop down quickly to the bottom of your inbox, replaced by lots of new messages.

With its new Snooze feature, MailButler offers a solution for this.

If you want to deal with the email later, you just have to press the “Snooze” button, and set up the time, when you want to get back to it: be it in several hours, or at the beginning of the next workday.

After that the email is removed from your inbox and moves back to the top later, right at your preferred time. Snoozed emails are also available for you the entire time in a special folder. Zero Inbox has never been so easy to reach before!

MailButler is an add-on, which adds many great productivity boosting features to your Apple Mail. Among them is the ability to schedule, undo, and track emails, convert emails to notes, upload email attachments regardless of size, and more. The developers plan to add new things to this list.

You can learn more about the features and also download the program here.

]]>
-
- - Katie's Week In Review: August 7, 2016 - Katie Floyd - Sun, 07 Aug 2016 16:08:30 +0000 - http://tracking.feedpress.it/link/980/4087132 - 50c628b3e4b07b56461546c5:50c658a6e4b0cc9aa9ce4405:57a75ce8ebbd1acee61ea021 - We continue in the lull of summer with not a whole lot of exciting tech news to report. Nevertheless I did manage to find a few interesting links of note for the week ending August 7, 2016:

  • Apple released iOS 9.3.4 this week with a single security fix. Notably, this patch fixes the “Pangu jailbreak exploit” related to a memory corruption issue.
  • On the topic of security, Rene Ritchie of iMore reports that Apple has created a new security bounty program which launches in September offering cash rewards for reporting of security vulnerabilities. iMore has an FAQ on the program.
  • Apple released their long awaited Remote App for the fourth Generation Apple TV. Instead of an update to their existing App, this is an entirely new application available for iPhone. There are some pros and cons of the new application, Dan Moren, writing for Six Colors, has an overview.
  • Writing for Recode, Peter Kafka says that Apple is working on a new digital TV Guide type system to tell users what’s on Apple TV and how to watch it. According to Recode, the idea is to let users see what kind of programing is available through their various video apps and services without having to open the individual applications. Supposedly the “single sign-on” service previewed at WWDC earlier this year was the first step of the plan. I can’t help but think that TiVo does something similar with their “OnePass” feature that allows users to browse and record shows from a variety of different services.
  • The 2016 Olympics kicked off in Rio this week. Though I’m not a huge sports fan, I do like to watch the highlights. I’m primarily watching NBC’s primetime coverage using my OTA Antenna. However, watching the Olympics can be a problem for cord cutters, especially if you want to watch the games live, not delayed. Michael Ansaldo writing for TechHive has put together a guide for how to watch the Olympics without paying for cable TV.
  • Logitech just released a new Create Keyboard for the 9.7" iPad Pro. Oscar Raymundo at Macworld takes a first look. This cover features backlit keys, has a row of iOS specific shortcuts, and includes a protective folio case with holder for the Apple Pencil. I just bought the Apple SmartKeyboard and back case a couple of weeks ago. Although I’m happy with my setup, I might have opted to try the Logitech had I waited a few more weeks.
  • Federico Viticci at MacStories highlights a problem with Stolen iPhones and Identity Theft and shares the story of very convincing phishing attempt his mother received after having her iPhone stolen a few weeks prior.
  • One of my favorite Apps, 1Password has introduced a new subscription service for individuals. (Don’t worry, the standalone service isn’t going away for those of you that are happy with that service.) The new subscription service offers some additional features including built-in automatic sync, data loss protection, web access to all your data on 1Password.com and includes access to all the the most updated versions of all the 1Password applications. Pricing for an individual plan is $2.99 a month with a special launch offer offering 6-months free.
  • The OmniGroup also announced a major update to an App I use regularly, OmniFocus now supports end-to-end encryption. Thanks to recent updates to all the apps on their platform, data Synced to the OmniFocus servers are now securely encrypted when at rest on the server. One you’ve updated all your devices, you’ll receive a notification that your database is available for upgrade.
  • Finally this week, for years the original model of the U.S.S. enterprise has hung in the Smithsonian National Air and Space Museum. However, for the past several it’s been relegated to the gift shop as the model has deteriorated. The Enterprise was recently restored to its original glory and is now back on display in the Milestones of Flight Hall. Here’s a video showing the process of the restoration.
]]>
-
- - First Look: MOS Go USB-C Battery Pack - Katie Floyd - Sat, 06 Aug 2016 23:29:49 +0000 - http://tracking.feedpress.it/link/980/4081735 - 50c628b3e4b07b56461546c5:50c658a6e4b0cc9aa9ce4405:57a6720337c58153c17e089d - - - - - - - - - - -

Charging a laptop with a battery pack was something that has previously only been done with expensive adapters. With more laptops switching to USB-C for power, battery packs similar to the ones we carry to recharge our iOS devices can now be used to recharge computers. Of course, not all batteries are created equal. Most batteries designed for cell phones and tablets will not be able to keep up with the power demands required by laptops and may only provide a minimal charge to a USB-C notebook, and only then if the notebook is powered off.

The MOS Go is a battery with 12,000 mAh, 44.4 Wh capacity which the manufacturer claims will recharge a 2015 MacBook up to 75% capacity. More importantly, it will also provide power to a MacBook that is powered on, meaning you don’t have to shut down your computer to recharge the battery. I viewed a video demonstration provided by the manufacturer where a MacBook with the screen at 50% brightness was connected to the MOS Go and was able to gain about 30% charge over 30 minutes.

The MOS Go has a single USB-C input and thus requires a USB-C cable to charge, but has both standard USB and USB-C outputs. This means you can recharge both your USB-C laptop as well as an iPhone or iPad with a single battery pack. With a 12,000 mAh capacity, the MOS Go should charge most smartphones up to four times and most tablets twice.

I’ve been playing with a MOS Go for about a week now and it’s a nice battery. The build quality is solid and I like that it has dual USB and USB-C outputs. My only wish is it has both USB and USB-C inputs for recharging. If you have a USB-C capable device, like the new MacBook or one of Google’s Chromebooks, adding a USB-C power pack to your travel bag is something worth considering. You can order the MOS Go direct from the manufacturer for $119.95. It’s also available on Amazon.

Now, if only I could get Apple to release a new MacBook Pro with USB-C ports…

]]>
-
- - Time Warner Buys 10% Stake in Hulu, Now Don't Screw This Up - Katie Floyd - Wed, 03 Aug 2016 15:47:36 +0000 - http://tracking.feedpress.it/link/980/4052707 - 50c628b3e4b07b56461546c5:50c658a6e4b0cc9aa9ce4405:57a211806b8f5b9351bb7efd - - - - - - - - - - -

As reported by CNET, Time Warner has bought a 10% stake in Hulu and as part of the deal will be bringing some of its content, including live broadcast TV to Hulu. According To CNET:

The streaming service, which is owned by three of the country’s main broadcast networks, plans to launch a paid option next year that broadcasts live TV over the internet. On Wednesday, Time Warner’s Turner bundle of networks became the first to sign on. Turner channels to be available live on Hulu include TNT, TBS, CNN, Cartoon Network, Adult Swim, TruTV, Boomerang and Turner Classic Movies.
The deal doesn’t include Time Warner crown jewel HBO, and it doesn’t put any on-demand shows from the company on Hulu’s Netflix-like service of TV repeats and originals.

As a “cord cutter” I’m excited about this deal, but nervous that they may “screw it up.” Especially during big news events (like election season) one of the things I miss is having access to cable news, like CNN. CNN allows cable TV subscribers to stream their content live over Apple TV, but there’s currently no (authorized) way for those of us without subscriptions to access live content. I’ve long said I’d love the option to pay somewhere between $2 - $5 month to be able to buy streaming CNN as an a la cate option.

Depending on the final price of this plan with Hulu, it may be an option. I just hope they don’t screw it up by bundling together a bunch of channels and services I don’t care about and over-charging. That’s what ultimately keeps me from subscribing to services like Sling.

]]>
-
- - Mac Power Users #333: The Amazon Lifestyle - Katie Floyd - Tue, 02 Aug 2016 22:14:00 +0000 - http://tracking.feedpress.it/link/980/4045901 - 50c628b3e4b07b56461546c5:50c658a6e4b0cc9aa9ce4405:579f75a52e69cfd777c37618 - I love Amazon. It has become my default place for all my online shopping and has also replaced a lot of my local shopping as well. This week on Mac Power Users, David and I dive deep on the Amazon lifestyle. We explore many different services and features available from Amazon and share our favorite tips and tricks for how to make the most of them.

This episode of Mac Power Users is sponsored by:

  • Squarespace: Enter offer code MPU at checkout to get 10% off your first purchase.
  • Igloo: An intranet you’ll actually like, free for up to 10 people.
  • PDFpen from Smile With powerful PDF editing tools, available for Mac, iPad, and iPhone, PDFpen from Smile makes you a Mac Power User.
  • Fracture Bring your photos to life.

Permalink

]]> - - - ScreenCastsOnline Monthly Magazine: Review of Eero - Katie Floyd - Mon, 01 Aug 2016 16:10:55 +0000 - http://tracking.feedpress.it/link/980/4033735 - 50c628b3e4b07b56461546c5:50c658a6e4b0cc9aa9ce4405:579f74bc8419c2adac587a7f - This month’s issue of ScreenCasts Online Monthly Magazine is now available in ScreenCasts Online Magazine App. In the August issue you’ll find an article from me about Eero wireless access points

The monthly magazine is packed with streamable versions of Don’s excellent video tutorials as well as articles, reviews and tips from authors including David Sparks, Allison Sheridan, Wally Cherwinski and more. The magazine is free for ScreenCasts online Premium Members or available as a separate subscription or you can pickup individual issues. You can download it in the AppStore or find more info at https://www.screencastsonline.com/membership_benefits/

]]>
-
- - Katie's Week in Review: July 31, 2016 - Katie Floyd - Sun, 31 Jul 2016 21:47:57 +0000 - http://tracking.feedpress.it/link/980/4026944 - 50c628b3e4b07b56461546c5:50c658a6e4b0cc9aa9ce4405:579e71f020099e276ae4782d - This week saw Apple’s Third Quarter financial results and a billion iPhones sold. Here are the links of note for the week ending July 31, 2016:

  • Apple reported its Third Quarter Results. You can find the official report on Apple’s website. Rene Ritchie and Mikah Sargent of iMore have prepared a transcript of the call and Jason Snell of Six Colors has a slew of charts and commentary breaking down the quarter. Although the year-over-year numbers for iPhone were down, probably due to the amazing success of the iPhone 6 last year, Apple did manage to beat expectations for the quarter. The third quarter is traditionally Apple’s worst. Next quarter we’ll hopefully have a bump from “back to school” sales, but right now there’s not a lot of new product out there for people to buy.
  • Apple also announced this week that it sold its one billionth iPhone. Yes, that’s billion with a “B”. When Apple announced the iPhone less than 10 years ago Steve Jobs said they were just hoping to capture a portion of the phone market. Turns out they’ve managed to create a product that completely changed the world.
  • Speaking of the new iPhone, it seems like a pretty sure deal that the new iPhone released this fall will lack a headphone port. As reported by Jeff Gamet of The Mac Observer a new video making the rounds on the Internet claims to show Apple’s own Lightning to 3.5mm headphone jack adapter. While I have no doubt that Apple will release such an adapter or something similar, I doubt they will ship it in the box as doing so would 1) almost be an admission that removing the headphone jack might have come too soon and 2) negate the opportunity for add one sales. I bet we’ll see another $9.99 - $19.99 adapter as an opportunity for an add-on purchase.
  • As reported by Chance Miller of 9to5Mac, Apple has purchased the rights to ‘Carpool Karaoke’, a popular Late Late show segment and will release new episodes via Apple Music. Aside from Beats One, this is really the first time we’ve seen Apple purchase content for redistribution and seems like an odd move. We’ll see where this goes.
  • Daisuke Wakabayashi of the Wall Street Journal reports that Special Consultant and former Apple Executive Bob Mansfield will oversee Apple’s Car Project. Bob will supposedly be working directly with Jony Ive and senior managers on the car project are now reporting directly to Bob. If this is true, it sounds like things may be getting real.
  • While the Apple Car is likely a ways off, Harish Jonnalagadda reporting for iMore says that Ford will be integrating CarPlay into all of its 2017 vehicles. CarPlay will come as part of “Sync 3” which Ford has announced will be integrated in all of its 2017 cars, SUVs, light trucks and electric vehicles.
  • Kara Swisher of Recode reports that Verizon will buy Yahoo for $4.83 billion with the core business being merged into AOL. Verizon will keep the Yahoo brand. It’s hard for me to get excited about any Yahoo news since I’ve long since stopped using any of Yahoo’s core products or services, given the relatively low acquisition price it sounds like a lot of other people have too.
  • As reported by Thomas Reed of Malwarebytes Safe Mac Blog, there’s new cross-platform malware, Adwind, which can also infect Macs. The Malware requires the installation of Java, which does not come installed natively on the Mac, and isn’t code signed, so Gatekeeper will top it, but it’s out there nonetheless.
  • Adam Engst writing at TidBITS alerted me to another possible vulnerability. Apparently you can Disable Find My Mac by resetting the NVRAM which is ridiculously easy to do. This can be prevented by setting a Firmware password, which has it’s own risks and benefits. Things to know.
  • Finally this week, not really tech related (Okay, Netflix related) but one of my favorite TV Series of all time is Gilmore Girls. A new “Year in the Life” series is coming to Netflix as a 4-part special and premiers on November 25. I love all my favorite shows and movies are all coming back again. Let’s just hope this is better than the X-Files reboot. Here’s the teaser announcement:
]]>
-
- - Early Bird Registration Open for MacTrack Legal - Katie Floyd - Sat, 30 Jul 2016 15:39:53 +0000 - http://tracking.feedpress.it/link/980/4016962 - 50c628b3e4b07b56461546c5:50c658a6e4b0cc9aa9ce4405:579cca2046c3c4b32bed5f74 - - - - - - - - - - -

Formally known as MILOfest, MacTrack Legal is one of the the best tech and legal practice conference for Mac-loving lawyers. It is a conference for attorneys interested in Macs, iPhones, iPads and anything else bearing an Apple logo. The conference features an interesting mix of practical tips, substantive education.

This event gathers the most collegial and generous attorneys from across the country (and even overseas) in a fun and information multi-day program. The event will be held at Disney’s Yacht and Beach Club in Orlando, Florida October 6–8, 2016. I’m planning to attend and speak at the conference again this year and I hope to see some of you there.

Early Bird pricing is currently available and there’s a special discount if you’ve attended MILO fest at least twice in the past seven years. You can learn more at http://mactracklegal.com

]]>
-
- - Mac Power Users #332: Other Uses For Keynote - Katie Floyd - Tue, 26 Jul 2016 22:59:00 +0000 - http://tracking.feedpress.it/link/980/3980262 - 50c628b3e4b07b56461546c5:50c658a6e4b0cc9aa9ce4405:57975e9a46c3c49c534d4c9a - We all know Keynote is a great app for giving presentations but it can do so much more. This week on Mac Power Users, David and I explore the many uses for Keynote beyond presentation day.

This episode of Mac Power Users is sponsored by:

  • TextExpander from Smile Type more with less effort! Expand short abbreviations into longer bits of text, even fill-ins, with TextExpander from Smile.
  • 1Password Have you ever forgotten a password? Now you don't have to worry about that anymore. 
  • The Omni Group We're passionate about productivity for Mac, iPhone and iPad. 
  • Backblaze: The unlimited, native backup solution for your Mac and PC

Permalink

]]> - - - 10 Great Apps for Productivity - Katie Floyd - Tue, 26 Jul 2016 12:55:01 +0000 - http://tracking.feedpress.it/link/980/3976778 - 50c628b3e4b07b56461546c5:50c658a6e4b0cc9aa9ce4405:57975b4903596ebfc9a4d3c5 - I'm an attorney by day and others in my local legal and business community have come to know me as a "techie".  Just last week while at a meeting of the probate section meeting a colleague leaned over and wanted to know about several of the Apps on my iPad home screen.  As someone who is regularly asked about the apps I'm using for my work, I thought I'd put together a list of some of my favorites. To start, here are my ten of my favorite apps geared towards professionals. 

1. Due - $4.99 - http://www.dueapp.com - Perhaps I have a problem with reminder/task management apps. You’ll notice I several on this list, but they all serve a different purpose. Due is a reminders and timers App that I use for the specific purpose of reminding me for things that absolutely have to be done, or have to be done at a specific time. The problem with traditional reminder apps is you’ll typically get a notification of a task, but that’s it. If you’re busy at the moment the notification pops up, you’re likely to forget about ti and move on. Due will repeatedly remind you of items that are due until you act on them. You set the reminder interval. For extremely urgent items, you can set an interval as short as 1 or 5 minutes. For not so urgent items, you may only want to be reminded ever 30 minutes or an hour. 

- - - - - - - - - - - - - - - -

2. Scanner Pro - $3.99 - https://readdle.com - I go back and forth with scanner apps, but currently Scanner Pro is my favorite. I try to adopt a paperless lifestyle so whenever possible I try to scan receipts, papers and other bits of paper I come across in my life so I can get rid of them. Scanner Pro is a great all around scanner App but has recently added the ability to OCR documents and create a default name based on the date as well as create custom workflows for processing specific items. For example, I have a workflow that will process business receipts to a specific Dropbox folder, then delete the receipt once saved.

3. Fantastical - $4.99 – https://flexibits.com/fantastical-iphone - Fantastical has replaced the Calendar App on my iPhone for time management. The big feature of Fantastical is natural language parsing. I can type out a phrase such as “Lunch with David Tuesday at Taco Toms” and Fantastical will automatically create an appointment at noon on the next Tuesday and set the location as Taco Toms and attempt to locate and add the address of the restaurant. 

- - - - - - - - - - - - - - - -

4. Cloak VPN - Varies - http://www.getcloak.com if you use free Wi-Fi hotspots such as in coffee shops or at hotels and conference centers you need to use a VPN to secure your Internet traffic from prying eyes. Cloak is probably my favorite of the VPN solutions because they offer solutions for Mac and iOS and have a feature that will auto secure a connection to an unknown wireless network so I don’t have to think about it. As soon as I walk into Starbucks and connect, Cloak automatically activates and secures my connection. There are a couple of price options available. I keep a “mini” subscription active for a couple bucks a month that gives me 5GB of access each month which is good for occasional usage. If I’m traveling and need more data, I can add on data as needed.

5.Microsoft Office – Varies - https://products.office.com/en-us/mobile/office-iphone Microsoft Office is a suite of products that has been a pleasant surprise on iOS. I most often use Word.app on my iPad and have found it to be remarkably full featured and stable. For all devices except the iPad Pro (due to it’s screen size larger than 10") most of the features of the office suite such as editing and saving documents can be unlocked with a free Microsoft account. The products also integrate well with cloud services such as Dropbox and Microsoft’s own OneDrive.

- - - - - - - - - - - - - - - -

6. Duet Display - $9.99 - http://www.duetdisplay.com - I love having a second monitor and find it’s a great productivity tool. However, when I’m traveling or using my laptop on the go, that isn’t possible - unless you have Duet Display. This App allows you to use your iPad or iPhone as an extra display and will extend the desktop of both Macs and Windows PCs. 

7. PDFpen - $19.99 - https://smilesoftware.com/pdfpen-ios - I’ve tried several PDF apps on iOS and PDFpen is still my favorite, probably because it syncs so well with PDFpen on the Mac. Especially since I’ve upgraded to a 9.7" iPad Pro, I find myself viewing and editing PDFs more on the larger size iPad and viewing PDFs in split screen for reference while working in another document. I love PDFpen’s ability to edit PDFs and regularly find myself dropping my signature into PDFs on the go and sending them back to people.

- - - - - - - - - - - - - - - -

8. OmniFocus - $39.99/$59.98 - https://www.omnigroup.com/omnifocus - With recent updates OmniFocus on iOS has really come into its own as a standalone application rather than simply as a companion to the Mac App and it’s my task management system of choice. Just about anything that you can do in the Mac App can now be done in iOS and Omni has now even added support for automation which is great for setting up recurring complex tasks or a series of tasks. I’m a great fan of Siri integration which means OmniFocus can read items off my default Reminder’s list or a specific Reminder’s list and when items are added to that list (either through Siri or otherwise) they will automatically be added to my OmniFocus inbox.

9. Deliveries - $4.99 - http://junecloud.com/software/iphone/deliveries.html - With the exception of weekly grocery shopping, I’ve probably transitioned most of my shopping online. I buy almost all my electronics, clothing and convenience items online. This means I have a lot of packages coming to my door and sometimes can loose track of them. I use Deliveries to track shipments so I know when to expect them. My favorite feature is after buying the App you can sign up for a free account on their website, register your email address and from there you can forward delivery notification emails to a special email address and the delivery information will automatically show up in the app. It’s almost too easy.

- - - - - - - - - - - - - - - -

10. Todoist - Free/$28.99 a year - http://www.todoist.com - This is my third task management related App on this list, but it serves a specific purpose. While I love OmniFocus for my personal task management needs, I was missing a way to share and delegate tasks with others, particularly in the office. To solve this need, our office turned to Todoist. As a task management App it’s pretty basic, and candidly I’m not in love with the design or implementation. But the sharing and collaboration features are good and it allows me to share projects with other attorneys in my office as well as my legal assistant and assign tasks and check on the status of those tasks. For less than $30 a year per person for a business plan, the price was right to get our office started.

This article first appeared in the June Issue of ScreencastsOnline Monthly Magazine. ScreenCastsOnline monthly magazine is packed with hints, tips, articles and links to streamable versions of ScreenCastsOnline tutorials and delivered monthly via Newsstand on the iPad. You can find out more at https://www.screencastsonline.com/membership_benefits/

]]>
-
- - Katie's Week In Review: July 24, 2016 - Katie Floyd - Sun, 24 Jul 2016 19:51:38 +0000 - http://tracking.feedpress.it/link/980/3960924 - 50c628b3e4b07b56461546c5:50c658a6e4b0cc9aa9ce4405:57951c398419c25e8f05f8cd - We’re still in the dog days of summer, but there’s a little more Apple news to report. Here are the links of note for the week ending July 24, 2016:

  • Apple has released a slew of software updates this week including OS X 10.11.6, iOS 9.3.3, tvOS 9.2.2 and watchOS 2.2.2. The updates include primarily bug fixes but also several security updates. The downloads can be obtained through the software updated function on your hardware. I’ve updated all my devices and haven’t run into any issues.
  • Apple announced its FY 16 Third Quarter Results Conference Call will be held on Tuesday, July 26th at 2pm Pacific, 5pm Eastern. The call will be available as a webcast and also available for two weeks after for replay on Apple’s website. Investors are a bit nervous about this call given Apple’s lackluster results last quarter and the somewhat negative rumors circulating about Apple’s upcoming iPhone 7. Let’s hope cooler heads prevail.
  • Speaking of the new iPhone 7, Juli Clover of MacRumors reports on a new mockup circulating around the Internet which depicts three models of the new iPhone including an iPhone 7, iPhone 7Plus and a new iPhone 7 Pro. The Pro model allegedly features a 5.5" form factor, dual lens and a smart connector. I’m personally a little skeptical about this rumor, but time will tell.
  • There was major acquisition news this week, as reported by Dan Frommer of Recode, SoftBank is reportedly buying ARM Holdings for $32 billion. This is notable not only for the size of the acquisition, but also because ARM chips dominate the smartphone market including Apple’s iPhone and iPads.
  • There have been several complaints since iTunes Match launched that the service was replacing “matched” songs with different versions. For example, users have reported live tracks could be swapped with studio versions or similar mistakes. Supposedly Apple has been working on audio fingerprint technology to improve the accuracy of this match. As Jim Dalrymple of the Loop explains, Apple has already been using audio fingerprint technology for iTunes Match subscribers and will now be rolling it out to Apple Music Subscribers. I subscribe to iTunes Match, but not Apple Music. I still see a frustrating number of unmatched tracks for no apparent reason, but hopefully as the technology improves this will decrease.
  • The popular writing App Scrivener is now available for the iPad and iPhone. We’ve spoken about Scrivener a lot on Mac Power Users, my pal David Sparks has written books on Scrivener and shares his thoughts on the new iOS App. (Spoiler alert, he likes it.)
  • Jeff Gamet of The Mac Observer has news that the new Star Trek Series is coming to Netflix Streaming, but not in the US. The new series will launch in 2017, but US viewers will need a paid CBS All Access account that costs $6 per month. Still, good news for those in the 188 countries where Netflix will be showing the series.
  • Drafts is one of my favorite iOS Apps, in fact its in my Dock, but I still don’t know many of the ninja tricks. Good news, my friend David Sparks has been working on a Screencast Series for Draft with over an hour of content. The series is now complete and there are over 15 tutorials in total available on the Drafts website. Here’s a sample:
]]>
-
- - Mac Power Users #331: Stephen Hackett: Collector of Macs - Katie Floyd - Thu, 21 Jul 2016 16:09:44 +0000 - http://tracking.feedpress.it/link/980/3937419 - 50c628b3e4b07b56461546c5:50c658a6e4b0cc9aa9ce4405:5790f38603596ea40a37aebd - This week on Mac Power users RelayFM co-founder, Apple Collector and YouTuber Stephen Hackett joins David and me to explain essential cloud-based services used to run the Relay Network, his growing collection of Macs, and his photo and video workflows.

This episode of Mac Power Users is sponsored by:

  • Hover: Simplified domain management. Use code AUTOMATION to get 10% off your first purchase.
  • The Omni Group We're passionate about productivity for Mac, iPhone and iPad. 
  • Automatic The connected car company that improves your driving and integrates your car into yoru digital life. Enter code MACPOWER to get 20% off your purchase. 
  • Fracture Bring your photos to life.

Permalink

]]> - - - Katie's Week in Review: July 17, 2016 - Katie Floyd - Sun, 17 Jul 2016 22:11:55 +0000 - http://tracking.feedpress.it/link/980/3903182 - 50c628b3e4b07b56461546c5:50c658a6e4b0cc9aa9ce4405:578c02988419c2c106ad40d3 - My apologies for skipping the Week in Review last week, but I had a fabulous time at my brother’s wedding. To make up for it, I have a little extra content this week, so without further delay, here are the links of note for the week ending July 17, 2016:

  • This week we saw a leaked video that allegedly shows the iPhone 7 rear casing. As noted by Juli Clover of MacRumors, the design features no headphone jack and a larger camera hole that may accommodate an improved image sensor.
  • Eddie Cue spoke with Natalie Jarvey of the Hollywood Reporter and shared some insight about future plans for the Apple TV and thoughts on video streaming services. Among other things, Cue says Apple isn’t in the business of trying to create TV shows but is open to “helping guide them and make suggestions.”
  • Apple has released public betas of iOS 10 and macOS Sierra. Although I recommend extreme caution when installing betas (and I suggest you don’t do this on any machine you rely on) Serenity Caldwell and Lory Gil of iMore has released guides on how to install iOS 10 and macOS Sierra public betas. If you decide to play with the beta, Jason Snell of Six Colors has 7 things to try in the macOS beta.
  • If you weren’t able to attend WWDC, Apple has released the videos of the sessions along with searchable transcripts. Watch and enjoy!
  • A new piece of Mac malware has been discovered. Thomas Reed writing for the Malwarebytes blog explains that this new malware is in the form of a fake document converter app that actually installs a backdoor to your Mac. As always, download software with caution.
  • Last week Apple announced it was bringing the National Organ Donor Registration to iPhone as part of an update to iOS 10. With the new released iPhone users will be able to sign up to be an organ, eye, and tissue donor from the Health.app. I’m already registered as an organ donor, but I’ll be updating my information in the Health App as soon as the new OS is released
  • I’m a big fan of two-factor authentication and try to enable it for all my apps and services. Recently Apple has changed the way they implement two-factor authentication. If you’re using the old system (a four digit code delivered via push notification or SMS) you may want to update to the new and improved method. If your confused (as I was) have no fear, Dan Moren writing at Six Colors walks you through the process.
  • This week Pokémon Go! swept across the world. I’m not a big gamer and have never had much of an interest in Pokémon, but it seems to be all the Internet can talk about. If your curious about what the rage is all about, Serenity Caldwell, Lory Gil and Jen Karner of iMore have created a Beginner’s guide on how to play Pokémon Go.
  • My thanks to MailButler for sponsoring KatieFloyd.com this week. MailButler is an add-on, which adds many great productivity boosting features to your Apple Mail. If you’re interested in promoting your App or service through a RSS sponsorship of this site, you can learn more here.
  • Finally, Stephen Hackett (who is our guest next week on MPU) has continued his video series. This time Stephen reviews 7 old Apple notebooks in 6 minutes.
]]>
-
- - Mac Power Users #330: I'm not familiar with "Wookieepedia" - Katie Floyd - Tue, 12 Jul 2016 23:45:00 +0000 - http://tracking.feedpress.it/link/980/3859869 - 50c628b3e4b07b56461546c5:50c658a6e4b0cc9aa9ce4405:5782ec45b8a79b369b3af426 - This week on Mac Power Users David and I catch up on listener feedback. MPU Listener Ruben shares how he uses his MPU workflows on his work PC, we discuss merging Apple IDs, cleaning up after being scammed, follow-up on iOS photography, family tech support and listeners share their tips and tricks.

This episode of Mac Power Users is sponsored by:

  • Igloo: An intranet you’ll actually like, free for up to 10 people.
  • Fujitsu ScanSnap ScanSnap Helps You Live a More Productive, Efficient, Paperless Life. 
  • Gazelle Sell your iPhone for cash at Gazelle! 
  • Sanebox Stop drowning in email!

Permalink

]]> - - - diff --git a/Frameworks/RSParser/RSParserTests/Resources/OneFootTsunami.atom b/Frameworks/RSParser/RSParserTests/Resources/OneFootTsunami.atom deleted file mode 100755 index ecf9c69fd..000000000 --- a/Frameworks/RSParser/RSParserTests/Resources/OneFootTsunami.atom +++ /dev/null @@ -1,673 +0,0 @@ - - One Foot Tsunami - Slightly less disappointing than it sounds - - 2015-09-08T14:21:41Z - - - http://onefoottsunami.com/feed/atom/ - - - WordPress - - - - Paul Kafasis - - Link: <![CDATA[Pillow Fight Leaves 24 Concussed]]> - - - - - http://onefoottsunami.com/?p=14863 - 2015-09-07T18:14:11Z - 2015-09-08T14:21:41Z - At West Point, freshman cadets have long had an annual massive nighttime pillow fight to build esprit de corps. This year, it turned violent.


∞ Permalink]]> - - - - - - - Paul Kafasis - - <![CDATA[Perverse Incentives]]> - - http://onefoottsunami.com/?p=14861 - 2015-09-07T12:21:22Z - 2015-09-07T12:21:01Z - In China and Taiwan, drivers who’ve hit someone with their car may attempt to kill the person. Why?

- -

[I]f you cripple a man, you pay for the injured person’s care for a lifetime. But if you kill the person, you “only have to pay once, like a burial fee.”

- -

Because the legal system has often failed to prosecute these murders, a perverse incentive has been created. Once a driver hits an individual, the financially prudent move is for him to kill the injured party, rather than allowing them to live with a severe injury.

]]>
-
- - - - - Paul Kafasis - - Link: <![CDATA[Space Jam Forever]]> - - - - - http://onefoottsunami.com/?p=14858 - 2015-09-04T14:31:25Z - 2015-09-04T14:31:31Z - In 2010, a user on Reddit discovered that the website for the 1996 movie “Space Jam” was inexplicably still online. Almost 5 years later, and nearly 20 years after the film was released, that’s still true. It’s a wonder to behold. Now, Rolling Stone has done a wonderful archaeological dig on a piece of the ancient Internet, well preserved.


∞ Permalink]]>
-
- - - - - - Paul Kafasis - - <![CDATA[Head of the Charles]]> - - http://onefoottsunami.com/?p=14854 - 2015-09-03T03:34:24Z - 2015-09-03T14:32:12Z - As America grew in the 1800 and 1900s, many of our waterways became incredibly polluted. Cleveland’s Cuyahoga River actually managed to catch fire on multiple occasions. Likewise, Boston’s Charles River was famously polluted by both sewage runoff and industrial wastewater. That impression of a foul waterway is now deeply engrained in the minds of locals, reinforced constantly by the oft-heard classic Standells’ song “Dirty Water”.

- -

However, Herculean efforts made since 1995 have improved the river’s quality. Recently folks have even begun swimming in the Charles again, at least when water quality permits it. The EPA has tracked the steady improvement, now rating the Charles as one of cleanest urban rivers in America.

- -

'I Swam The Charles' Bumper Sticker -
[Photo courtesy of P. Kafasis]

- -

Still, does anyone actually want to drink water from the Charles River? Boston-based beermaker Harpoon aims to find out. Their new “Charles River Pale Ale” contains a not-so-secret ingredient: 300 gallons of Charles River water. While locals are likely gagging and reflexively spitting at the very thought, Harpoon is assuring the public the suds will be both safe and delicious.

- - -

In fact, the water was treated by Desalitech, a local water desalination company, purified to make it ideal for drinking. So taking a little taste of the Charles, at least in this form, probably won’t kill you. And hey, if/when you survive the experience, you can wear a shirt in this vein:

- -

Milton from Office Space in a shirt reading 'I Ate The Worm!!' -
I Drank the River!!

]]>
-
- - - - - Paul Kafasis - - Link: <![CDATA[No More Sense Than an Amish Bus Driver]]> - - - - - http://onefoottsunami.com/?p=14852 - 2015-09-02T17:32:10Z - 2015-09-02T17:31:10Z - Back in June, supporters of marriage equality declared victory in America with the Supreme Court’s ruling on Obergefell v. Hodges. Of late, Kentucky county clerk Kim Davis has been making news by defying the authority of the highest courts in the land, refusing to issue any marriage licenses whatsoever. Surprise surprise, Ms. Davis is a tremendous hypocrite, as US News reports:

- -

The marriages are documented in court records obtained by U.S. News, which show that Rowan County Clerk Kim Davis divorced three times, first in 1994, then 2006 and again in 2008.

- -

She gave birth to twins five months after divorcing her first husband. They were fathered by her third husband but adopted by her second. Davis worked at the clerk’s office at the time of each divorce and has since remarried.

- -

If Ms. Davis doesn’t wish to do her job, she should resign or expect to be removed, as John Corvino ably discusses:

- -

If [Davis’s] conscience renders her unable to issue marriage licenses to those legally qualified, then the right thing for her to do is resign. After all, issuing marriage licenses is not a peripheral, non-essential part of being county clerk — it’s a central job function. Her current stance makes no more sense than that of an Amish person who expects to retain a job as a bus driver.

- -

Here’s hoping this odious woman is found in contempt of court tomorrow, and quickly removed from her job, or at least from the spotlight.


∞ Permalink]]>
-
- - - - - Paul Kafasis - - Link: <![CDATA[Self-Driving Cars vs. Fixed-Gear Bikes]]> - - - - - http://onefoottsunami.com/?p=14850 - 2015-09-01T16:22:03Z - 2015-09-01T16:22:00Z - When a rider on a fixed-gear bicycle arrived at an intersection with a Google self-driving car, the car didn’t quite know what to do.


∞ Permalink]]>
-
- - - - - Paul Kafasis - - Link: <![CDATA[That’s Not What Dolphins Do]]> - - - - - http://onefoottsunami.com/?p=14848 - 2015-08-31T15:38:01Z - 2015-08-31T15:37:47Z - Surfer Elinor Dempsey didn’t catch any waves when she hit the ocean on Saturday, but she did get a pretty good story. As she waited in the water, something approached her from underneath.

- -

“First I thought it was a dolphin and I thought, ‘What the hell is he doing?’ ” she said. “And he kind of landed on my board. Then I realized he had taken a chunk. And I was, like, that’s not what dolphins do.”

- -

Shark Bite photo -
Better the board than her hand

- -

That is indeed not what dolphins do.


∞ Permalink]]>
-
- - - - - Paul Kafasis - - Link: <![CDATA[It’s Also a Very High-End Hunting Load for Ducks, Geese, or Turkeys]]> - - - - - http://onefoottsunami.com/?p=14846 - 2015-08-28T00:41:21Z - 2015-08-28T14:41:06Z - Well of course company is selling ammunition specifically marketed for shooting down drones. Of course they are.


∞ Permalink]]>
-
- - - - - Paul Kafasis - - Link: <![CDATA[Waste Not, Want Not]]> - - - - - http://onefoottsunami.com/?p=14839 - 2015-08-27T17:16:50Z - 2015-08-27T17:15:34Z - What do you do when you buy an expensive bottle of cognac but you’re not permitted to take it on your flight? Maybe you throw it out. You might gift it to a stranger. Perhaps you could throw an impromptu party in the airport. You’d be sure to make some new friends. Whatever you do, avoid the path taken by one Ms. Zhao:

- -

[S]he sat down in a corner and drank the entire bottle of cognac herself.

- -

As you’d probably imagine, this did not turn out well.


∞ Permalink]]>
-
- - - - - - Paul Kafasis - - <![CDATA[At Least He Can Still Go to the Same Church]]> - - http://onefoottsunami.com/?p=14836 - 2015-08-26T15:43:55Z - 2015-08-26T14:43:17Z - We open with a montage of newspaper articles, quickly showing JIMMY McGINTY’s criminal arc. - -

JIMMY is caught. -
(“NOTORIOUS MOB KILLER JIMMY McGINTY ARRESTED!”)

- -

He turns state’s evidence and aids the prosecution. -
(“McGINTY TESTIFIES AGAINST LEFTY HANNIGAN”)

- -

The trial brings down the last vestiges of the Boston Mafia. -
(“LEFTY HANNIGAN SENTENCED TO LIFE”)

- -

Finally, he disappears. -
(“WHERE IS JIMMY McGINTY NOW?”)

- -

CUT TO:

- -

INT. STEREOTYPICAL ITALIAN RESTAURANT – NIGHT

- -

Open on the CHEF, a older man with pale, freckled skin and bright red hair noticeably peeking out from under his chef’s hat. He is wearing an apron and a bushy mustache that is quite clearly fake.

- -

The CHEF approaches—

- -

A TABLE dressed in a red checkered tablecloth, with a candle lit atop it.

- -

—Where a lone female CUSTOMER, sits wearing a simple gray dress. She is persuing the menu.

- -
- -

CHEF -
(in a ridiculously over-the-top Italian-American accent — think “It’sa me, Mario!”) -
Buonasera, bella! What-a you like-a to have tonight?

- -

CUSTOMER -
(hesitant) -
Well, I’m not sure. I thought this was an Italian restaurant…

- -

CHEF -
(with delight) -
Oh, sì, sì! It is, it is!

- -

CUSTOMER -
(still hesitant) -
But I don’t recognize any of these dishes. “Black pudding”? “Limerick Ham”? “Corned Beef and Cabbage”, now that’s an Irish dish!

- -

CHEF -
(shaken, slips into a very real Irish-American accent) -
Ah, no, no, cailín -
(Quickly recovering his over-the-top Italian-American accent) -
Err, we Italians have-a that as well! But I-a tell you what. I’m-a gonna make you the specialty of the house! You-a trust me, no? After all, it’sa me, Mario! I own-a this place!

- -

CUSTOMER -
(relieved) -
Well, alright. That sounds lovely. Thank you, Mario!

-
- -

MARIO walks quickly to the back, through the inward swinging right kitchen door, then immediately back out the outward swinging left kitchen door with a tray he carries with two hands.

- -

On the tray is a plate which appears to contain a large tortilla covered in chunky tomato soup, with grated orange American cheddar cheese cooked on top. It is a comically poor imitation of Italy’s most famous dish.

- -

MARIO places the tray on the CUSTOMER’S table with a flourish.

- -
- -

CHEF -
Buon appetito!

- -

CUSTOMER -
(Staring at the plate, extremely hesitant) -
Uh… -
(Now staring intently at “MARIO”, noticing his red hair and fake mustache) -
What do you call this dish, “Mario”?

- -

MARIO -
(a ridiculously over-the-top Italian-American accent) -
Ah, you are not-a the first person to ask! In fact, a-so many a-people ask, I name-a the restaurant after a-my reply!

- -

PULL BACK TO RESTAURANT EXTERIOR, REVEALING THIS SIGN

-
-

That'sa Pizza!

]]>
-
- - - - - Paul Kafasis - - Link: <![CDATA[Tomato Seasoning]]> - - - - - http://onefoottsunami.com/?p=14832 - 2015-08-25T04:38:31Z - 2015-08-25T13:38:17Z - Over in Israel, Heinz is no longer allowed to sell its most popular condiment as “ketchup”.

- -

Heinz will no longer be allowed to label its red sauce as “ketchup” in Hebrew in Israel after local food manufacturer Osem successfully argued that its competitor’s product doesn’t meet the definition of Israel’s standards institute, Israeli news site Ynet reported.

- -

Instead, Heinz must use the ridiculous euphemism “tomato seasoning” to label their product. Then again, given that “tomato seasoning” sounds like the classy way a fancy restaurant would refer to the side they bring with their “pomme frites”, I’m not sure how much of a punishment this really is.


∞ Permalink]]>
-
- - - - - Paul Kafasis - - Link: <![CDATA[“I Love the Thing That I Most Wish Had Not Happened”]]> - - - - - http://onefoottsunami.com/?p=14829 - 2015-08-24T07:23:02Z - 2015-08-24T13:40:35Z - GQ has a rather wonderful piece on the once and future Stephen Colbert, discussing the future of late night, a past full of loss, and being present in the present.


∞ Permalink]]>
-
- - - - - Paul Kafasis - - Link: <![CDATA[The UK’s Most Disappointing New Visitor Attraction]]> - - - - - http://onefoottsunami.com/?p=14825 - 2015-08-21T14:26:07Z - 2015-08-21T13:51:54Z - I’ve always found guerrilla artist Banksy interesting enough, particularly when selling his own pieces as fakes. However, his new creation is really something else. Over in England, he’s created a dystopian theme park named Dismaland, and it’ll be open to the public for the next month. Christopher Jobson reports in detail:

- -

The event has all the hallmark details of a traditional Banksy event from a shroud of ultimate secrecy (the event area was plastered in notices designating it as filming location for a movie titled Gray Fox) to general themes of apocalypse, anti-consumerism, and anti-corporate messages. However there’s one major deviation: the emphasis of Dismalanded is largely on other artists’ work instead of Banksy himself.

- -

This trippy piece alone is enough to make me jealous of those who can go:

- -

Ariel, Sort Of -
Do not adjust your eyeballs.

- -

I have to imagine Disney’s lawyers will be all over this whole thing. Heck, the park even bans them (“The following items are strictly prohibited: knives, spray cans, illegal drugs, and lawyers from the Walt Disney corporation.”). If you have a chance to go, it definitely seems worth it.


∞ Permalink]]>
-
- - - - - - Paul Kafasis - - <![CDATA[Pretty Damned Good for Around a Thousand Pixels]]> - - http://onefoottsunami.com/?p=14814 - 2015-08-21T03:21:12Z - 2015-08-20T13:43:08Z - Back in October, I wrote about Square Cash, my favorite service for both exchanging money with friends as well as being amused by the imagined negotiating process of an complete moron. Last year’s post provided me with a brief trickle of $1 referral bonuses, netting me something like $18, so naturally I’ve been itching for another chance to write about Square Cash. Square has since upped their referral bonus to $5 for both sides, so, ya know: Sign up for Square Cash and get yourself a Lincoln.1

- -

Anyhow, the Square Cash iPhone app was recently updated to include support for the Apple Watch. Our glorious future truly has arrived, because it’s now possible to send money to your friends right from your wrist. Open the app and tap your desired recipient to see a screen like this:

- -

The Apple Watch Square Cash app
“Select Amounts” is kind of a weird instruction.

- -

To send cash, you tap the relevant bills to add up to the desired whole number (no change!) you wish to send, then tap “Pay”. Within seconds, and without any further verification or chance of cancelling, your money will be flying off to someone else’s bank account. As your money wings away, there’s even a ridiculous animated image of dollar bills fluttering down.

- -

The Apple Watch Square Cash app
No matter how long you stare at this image, it won’t move, because it’s just a still. Feel free to cash $PBones to see the full animated version though.

- -

I’ve previously written that you do not have to make an Apple Watch app. However, good third-party apps for the watch are certainly possible.2 The Square Cash watch app is definitely well made, and it offers functionality I’ll describe as at least potentially useful, which means it’s better than most Apple Watch apps to date. Perhaps the best thing the Square Cash Apple Watch app does, however, is advance the cause of gender equality.

- -

Allow me to back up slightly. You may have seen a recent push to put Harriet Tubman on America’s $20 bill, fully replacing Andrew Jackson. Hey, according to this article, Old Hickory might not have minded the change:

- -

[Jackson] also hated paper currency and vetoed the reauthorization of the Second Bank of the United States, a predecessor of the Federal Reserve.

- -

This proposed change has also led to other women being considered for placement on American currency, and it appears that the next re-design of the $10 bill will at least provide Alexander Hamilton with a female co-star. That’s some progress, at least, but the idea of placing Harriet Tubman on the $20 has also laid bare some incredible stupidity. Take a deep breath and try to absorb this:

- -

Jimmy's Stupid Comment
I think it’s the exclamation point that really gets me.

- -

Oh jeez. I honestly try to avoid highlighting this sort of depressing idiocy. I figure it’s best to let it die quietly in the dark, rather than than live and spread in the light. Some things are just so feebleminded that they must be skewered, however, and this is one of those things. So, how dumb are you, Jimmy Pecoul? Let me count the ways.

- -

A list of the ways in which Jimmy Pecoul has shown off his ignorance, in increasing order of stupidity

- -
    -
  • Problem #1: Thinking that only presidents belong on our banknotes

    - -

    While this is wrong, I wouldn’t be surprised if a not-insignificant number of people believe this, and think that both Alexander Hamilton (on the $10 bill) and Benjamin Franklin (on the $100 bill) were presidents.

  • - -
  • Problem #2: Mistaking Harriet Tubman for Rosa Parks

    - -

    These are two entirely different woman, who are famous for their work winning progress in different areas (abolitionism for Harriet Tubman and civil rights activism for Rosa Parks), and who were active nearly a full century apart.

  • - -
  • Problem #3: Having no understanding of what Rosa Parks did

    - -

    Rosa Parks did not “stand up to bullies on a bus”. Rosa Parks defied a despicable law and set off the Montgomery bus boycott, which helped bring about the end of segregation in America.

    -
  • - -
  • Stupidity #4: Thinking he’ll stop using $20 bills

    - -

    I like to imagine how this might go. Jimmy would have to avoid just about every ATM in America, for one thing. The interactions with cashiers, waiters, bartenders and the like ought to be something to see as well. I’d give him a week managing to boycott the bill, and that’s being generous.

    -
  • - -
  • Stupidity #5: Believing that “most” people will stop using the $20 bill

    - -

    I doubt even one person in the entire country would stop using yuppie singles if the picture on them changed from Jackson to Tubman. Most? MOST? Jesus Crist.

    -
  • - -
-

We’ll just ignore the incredibly foolish acts of posting this publicly to Facebook for the whole world to snigger at, as well as thinking anyone gives a single good goddamn what his “vote” is on this matter, because if we don’t my head might explode. Let’s get back to Harriet Tubman and Square Cash (Square Cash!). Have another look at the buttons for selecting the amount of money you wish to send:

- -

The Apple Watch Square Cash app's buttons

- -

Each button features a pretty good portrait of the corresponding man who appears on that denomination’s bill, but Andrew Jackson on the $20 doesn’t look quite right.

- -

The face on Square Cash's $20 bill

- -

Of course, that’s not Andrew Jackson at all — it’s Harriet Tubman! The image appears to be based on an 1895 portrait of Mrs. Tubman which is part of the collection of America’s National Portrait Gallery. Here’s a side-by-side comparison:

- -

Pixel Tubman and Photo Tubman, side by side

- -

And here it is, blown up:

- -

Enlarged Pixel Tubman and Photo Tubman, side by side -
The pixel version has managed to turn that dour frown upside down.

- -

Not bad! Lest you have any lingering doubt as to the true identity of this image, Square has confirmed that the image does indeed represent Tubman, with a spokesman stating “We put Harriet Tubman on the $20 bill because she is an American hero”. Well done. It’s a small gesture, but it’s a good one nonetheless.

- -
- -

Update (August 20th, 2015): Square’s creative director Robert Anderson used his own accidental invention (the @-reply) to link me to a higher resolution version of the Tubman image. He also confirmed that the 1895 portrait seen above was indeed the inspiration for the cartoon version. Neat!

- -

A higher-res $20 Tubman -
A higher-res Tubman Twenty

- -
- -

Footnotes:

-
    -
  1. The bill, not the McConaughey-endorsed vehicle. -↩︎

  2. -
  3. I should note that the Apple Watch app from USAA has been updated since I mocked it in that post. Now, in addition to showing your account balance (and allowing you to refresh that account balance), the app will show any transactions from the last seven days. That’s actually mildly useful! -↩︎

  4. -
]]>
-
- - - - - Paul Kafasis - - Link: <![CDATA[Early Notes on the Ashley Madison Hack]]> - - - - - http://onefoottsunami.com/?p=14809 - 2015-08-19T16:13:25Z - 2015-08-19T16:13:24Z - Over at The Awl, John Herman writes about the Ashley Madison hack. The data from this hack appears to be close to being easily searchable by the public, and while the consequences of that have been joked about, they deserve closer consideration:

- -

I’m not sure anyone is really reckoning with how big this could be, yet. If the data becomes as public and available as seems likely right now, we’re talking about tens of millions of people who will be publicly confronted with choices they thought they made in private…Here were millions of people expecting the highest level of privacy that the commercial web could offer as they conducted business they likely wanted to keep between two people (even if a great number of the emails are junk, or attached to casual gawkers, the leak claims to contain nine million transaction records). This hack could be ruinous—personally, professionally, financially—for them and their families.

- -

While it would be easy to say that the people who used this site deserve whatever happens, the fallout from this hack will affect far more than just the users and the implications for the future are also well-worth considering.


∞ Permalink]]>
-
- - - - - Paul Kafasis - - Link: <![CDATA[Unlikely to Be a Viable Alternative]]> - - - - - http://onefoottsunami.com/?p=14807 - 2015-08-18T17:11:58Z - 2015-08-18T17:11:55Z - Police departments in Canada and the US are experimenting with a new, less-than-lethal use-of-force option. A device called “The Alternative” allows an officer to fire a single shot gun which may take down a suspect, without killing them. If the shot fails to take down the suspect, the officer’s gun returns to its normal, lethal state.

- -

It’s an interesting enough idea, and the physics of the device itself seem practical. However, it’s difficult to imagine officers attaching the device to their service weapon in the middle of a confrontation. Perhaps even less likely is police departments adopting a device nicknamed the “Clown Gun”.

- -

The Clown Gun Explained -
The silver ball was originally bright orange and resembled a clown’s nose.


∞ Permalink]]>
-
- - - - - Paul Kafasis - - Link: <![CDATA[How to Win Contests and Influence Mexican Soap Stars]]> - - - - - http://onefoottsunami.com/?p=14805 - 2015-08-17T17:37:20Z - 2015-08-17T17:37:14Z - Hunter Scott won a whole lot of contests via Twitter, all thanks to a bot.

- -

My favorite thing that I won was a cowboy hat autographed by the stars of a Mexican soap opera that I had never heard of.

- -

Few of the prizes were valuable, but the whole experiment is amusing, and that’s worth something.


∞ Permalink]]>
-
- - - - - Paul Kafasis - - Link: <![CDATA[Chicago Falcons]]> - - - - - http://onefoottsunami.com/?p=14803 - 2015-08-14T04:59:10Z - 2015-08-14T13:59:12Z - If you want to see ridiculous good photos of peregrine falcons living on a balcony in Chicago, look no further.

- -

Peregrine Stalking Image -
Peregrine Falcon on Patrol


∞ Permalink]]>
-
- - - - - Paul Kafasis - - Link: <![CDATA[You Reap What You Sow]]> - - - - - http://onefoottsunami.com/?p=14801 - 2015-08-13T04:52:36Z - 2015-08-13T13:44:30Z - Megyn Kelly is getting the short end of the stick from her network as they work to sooth the hurt feelings of one Donald Trump. It’s a deplorable situation all around, but at the same time, what did Kelly expect when she went to work for Fox “News”?


∞ Permalink]]>
-
- - - - - - Paul Kafasis - - <![CDATA[Please Report to the Principal’s Office]]> - - http://onefoottsunami.com/?p=14784 - 2015-08-11T18:30:28Z - 2015-08-12T14:34:52Z - Speaking of anxiety-inducing email subject lines, here’s a doozie:

- -
  • A chat about your bad photos?

- -

When Apple removed the Camera Roll feature of iOS, I briefly used an app called MyRoll. Now, the company is emailing me about a new app called Gallery Doctor, which claims to identify and help you remove bad photos, thereby saving you space on your iPhone. That’s great and all, but how about an uplifting introduction, instead of an email that feels like it’s chastizing me?

]]>
-
- - - - - Paul Kafasis - - Link: <![CDATA[There Is, of Course, No Gun]]> - - - - - http://onefoottsunami.com/?p=14798 - 2015-08-11T00:09:01Z - 2015-08-11T13:08:56Z - Michael Heald has written an incredible story of running a half-marathon behind the walls of Oregon State Penitentiary, where the phrase “The Wall” takes on a very different meaning.


∞ Permalink]]>
-
- - - - - Paul Kafasis - - Link: <![CDATA[Unwelcome and Superfluous]]> - - - - - http://onefoottsunami.com/?p=14796 - 2015-08-10T15:56:03Z - 2015-08-10T15:55:54Z - Writer Ralph Jones gets a lot of press releases. Recently, he started replying to all of them with the phrase “I love you”.


∞ Permalink]]>
-
- - - - - - Paul Kafasis - - <![CDATA[Get Off My Back, CVS]]> - - http://onefoottsunami.com/?p=14754 - 2015-08-02T15:49:38Z - 2015-08-07T14:16:37Z - Sometimes I receive an email that fills me with existential sorrow. An email with a subject like:

- -
  • Paul, Are You Making the Most of Our App?

- -

Well god, CVS, I guess I really just don’t know. Am I? The implication is clearly that I’m not making the most of your goddamned app. I don’t need this pressure though, man. Hell, you’re clearly aware that I have the app.

- -

CVS Image

- -

But that’s not enough for you, is it? You’ve gotta tell me about the features of an app I already have installed and insist that I use them. But I’m not in the habit of printing out many photos anymore, and I’m not on any pills. You just let me use my iPhone as I like, and I’ll continue buying the assorted cold medicine, greeting cards, and clandestine bars of candy that I usually do. Deal?

]]>
-
- - - - - Paul Kafasis - - Link: <![CDATA[Drones Are Delivering Contraband to Prisons]]> - - - - - http://onefoottsunami.com/?p=14789 - 2015-08-06T14:43:29Z - 2015-08-06T14:43:28Z - Well of course drones are being used to deliver illicit goods into prisons.


∞ Permalink]]>
-
- - - - - - Paul Kafasis - - <![CDATA[Planes, “Planes”, and Automated Fare Pricing]]> - - http://onefoottsunami.com/?p=14756 - 2015-08-05T16:59:42Z - 2015-08-05T15:50:23Z - Let’s say that you wanted to get from Dayton, Ohio to central New Jersey, as friend of the site Chris DiNoia recently wanted to do. On United.com, you might select Newark as your destination, and get a result like this:

- -

Flying into Newark

- -

$353 is rather pricey for a one-way flight. Let’s check some other options. Philadelphia is about 30 minutes farther than Newark from central Jersey, and presumably you’re not a defenseless robot, so you should be safe there for a few minutes. Set Philadelphia as your destination instead, and hey, why not turn on the “Search Nearby Airports” checkbox?

- -

Flying into Philadelphia

- -

Hey, now there’s a better deal. You can get home for just $149! But hang on a sec. A close look shows that rather than Philadelphia International Airport (PHL), this flight winds up at “ZFV”, which is labeled as a “rail station”. How exactly is a plane going to land there?

- -

Examine this flight, and you’ll see something bizarre:

- -

Train Service -
“NOTE: This is Train Service” is a truly amazing warning.

- -

United Airlines is apparently code-sharing with Amtrak’s passenger railroad service to get you to Philadelphia. They’re also referring to Philadelphia’s 30th Street Rail Station (that’s what ZFV stands for) as an airport. Do you think the conductor announces that train as “United flight 3174”? Man, I hope so.

- -

But the wacky train-instead-of-plane isn’t even the half of it. Take a look at the first half of this itinerary, and compare it to the original search. It’s the exact same flight!

- -

Comparison

- -

This type of airline pricing nonsense is not entirely uncommon. Opting for the lower fare and then getting off at Newark even has a name, ”Hidden city ticketing”. Still, it’s not very often that you can save over $200 just by missing a train.

]]>
-
- - - - - \ No newline at end of file diff --git a/Frameworks/RSParser/RSParserTests/Resources/ScriptingNews.json b/Frameworks/RSParser/RSParserTests/Resources/ScriptingNews.json deleted file mode 100644 index 2d658e00f..000000000 --- a/Frameworks/RSParser/RSParserTests/Resources/ScriptingNews.json +++ /dev/null @@ -1,945 +0,0 @@ -{ - "rss": { - "version": "2.0", - "xmlns:source": "http://source.scripting.com/", - "channel": { - "title": "Scripting News", - "link": "http://scripting.com/", - "description": "Scripting News, the weblog started in 1997 that bootstrapped the blogging revolution.", - "pubDate": "Mon, 26 Jun 2017 19:40:58 GMT", - "lastBuildDate": "Mon, 26 Jun 2017 19:41:48 GMT", - "language": "en-us", - "copyright": "© 1994-2017 Dave Winer.", - "generator": "oldSchool v0.42c", - "docs": "https://github.com/scripting/Scripting-News/blob/master/rss-in-json/README.md", - "source:localTime": "Mon, June 26, 2017 3:41 PM EDT", - "cloud": { - "domain": "rpc.rsscloud.io", - "port": 5337, - "path": "/pleaseNotify", - "registerProcedure": "", - "protocol": "http-post" - }, - "source:account": [ - { - "service": "twitter", - "#value": "davewiner" - }, - { - "service": "facebook", - "#value": "dave.winer.12" - }, - { - "service": "github", - "#value": "scripting" - }, - { - "service": "linkedin", - "#value": "scripting" - } - ], - "item": [ - { - "link": "http://scripting.com/2017/06/26.html#a080605", - "description": "Good morning students and teachers! 🍏 ", - "pubDate": "Mon, 26 Jun 2017 12:20:05 GMT", - "guid": "http://scripting.com/2017/06/26.html#a080605", - "source:outline": { - "text": "Good morning students and teachers! :green_apple: ", - "created": "Mon, 26 Jun 2017 12:20:05 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/26.html#a080605" - } - }, - { - "link": "http://scripting.com/2017/06/26.html#a030658", - "description": "This is the human side of Health care is socialist. ", - "pubDate": "Mon, 26 Jun 2017 19:40:58 GMT", - "guid": "http://scripting.com/2017/06/26.html#a030658", - "source:outline": { - "text": "This is the human side of Health care is socialist. ", - "created": "Mon, 26 Jun 2017 19:40:58 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/26.html#a030658" - } - }, - { - "link": "http://scripting.com/2017/06/26.html#a020604", - "description": "Interesting Politico piece posits that Trump acts as if he's mayor of the United States. If NYC is his model, that mayor is esp powerless, because the governor of the state also has a lot of power over the city. It's approx 1/2 of the population of the state, and probably much more than 1/2 of the money. For example, the MTA, which runs the buses and subway, is run by the state, not the city.", - "pubDate": "Mon, 26 Jun 2017 18:10:04 GMT", - "guid": "http://scripting.com/2017/06/26.html#a020604", - "source:outline": { - "text": "Interesting Politico piece posits that Trump acts as if he's mayor of the United States. If NYC is his model, that mayor is esp powerless, because the governor of the state also has a lot of power over the city. It's approx 1/2 of the population of the state, and probably much more than 1/2 of the money. For example, the MTA, which runs the buses and subway, is run by the state, not the city.", - "created": "Mon, 26 Jun 2017 18:10:04 GMT", - "type": "outline", - "image": "http://scripting.com/images/2017/06/26/quimby.png", - "permalink": "http://scripting.com/2017/06/26.html#a020604" - } - }, - { - "link": "http://scripting.com/2017/06/26.html#a020602", - "description": "Brent asks if the length in enclosures in RSS-in-JSON is a number or string. That's what the test podcast below is for. ", - "pubDate": "Mon, 26 Jun 2017 18:05:02 GMT", - "guid": "http://scripting.com/2017/06/26.html#a020602", - "source:outline": { - "text": "Brent asks if the length in enclosures in \"RSS-in-JSON\" is a number or string. That's what the test podcast below is for. ", - "created": "Mon, 26 Jun 2017 18:05:02 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/26.html#a020602" - } - }, - { - "link": "http://scripting.com/2017/06/26.html#a010633", - "description": "From time to time I have to do a podcast to test things out. This is one of those times. Let's see what happens. ", - "pubDate": "Mon, 26 Jun 2017 17:29:33 GMT", - "guid": "http://scripting.com/2017/06/26.html#a010633", - "enclosure": { - "url": "http://scripting.com/2017/06/26/yetAnotherTestPodcast.m4a", - "type": "audio/mpeg", - "length": 277413 - }, - "source:outline": { - "text": "From time to time I have to do a podcast to test things out. This is one of those times. Let's see what happens. ", - "created": "Mon, 26 Jun 2017 17:29:33 GMT", - "type": "outline", - "enclosure": "http://scripting.com/2017/06/26/yetAnotherTestPodcast.m4a", - "enclosureType": "audio/mpeg", - "enclosureLength": "277413", - "permalink": "http://scripting.com/2017/06/26.html#a010633" - } - }, - { - "link": "http://scripting.com/2017/06/26.html#a110603", - "description": "Body shaming is wrong no matter who you're using as the example. Someone is being hurt by this. No, I don't care how much you have suffered.", - "pubDate": "Mon, 26 Jun 2017 15:26:03 GMT", - "guid": "http://scripting.com/2017/06/26.html#a110603", - "source:outline": { - "text": "Body shaming is wrong no matter who you're using as the example. Someone is being hurt by this. No, I don't care how much you have suffered.", - "created": "Mon, 26 Jun 2017 15:26:03 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/26.html#a110603" - } - }, - { - "link": "http://scripting.com/2017/06/26.html#a090616", - "description": "I need an app to view RSS feeds in the browser because Chrome and Safari refuse to let me do that. I'd love to hear the reason why. ", - "pubDate": "Mon, 26 Jun 2017 13:24:16 GMT", - "guid": "http://scripting.com/2017/06/26.html#a090616", - "source:outline": { - "text": "I need an app to view RSS feeds in the browser because Chrome and Safari refuse to let me do that. I'd love to hear the reason why. ", - "created": "Mon, 26 Jun 2017 13:24:16 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/26.html#a090616" - } - }, - { - "title": "Subscribable feed lists give power to users", - "link": "http://scripting.com/2017/06/26.html#a080636", - "description": "

An interesting comment from Chris Aldrich about subscribing to lists of feeds in a thread on the Woodwind app site on GitHub.

\n

Here's the basic idea. There's a difference between importing OPML into a reader and subscribing to it. The latter is very powerful, for the user, but a lot of RSS reader devs may not want their users to have that much power. It's not a very hard feature to implement.

\n

The idea has been much-discussed here. We call them reading lists. Michael Arrington even wrote a TechCrunch piece about it in 2005.

\n

Subscribable OPML is something all my readers have been able to do through an OPML feature called inclusion. I wrote a howto for a River5 user re inclusion just last week.

\n

Share Your OPML was a service I operated for a while. It made it possible to manage your OPML separate from the reader you used. It was meant to encourage readers to support subscribable OPML. I'm looking for an excuse to bring it back, but first we need a base of shared feed lists.

\n

A lot of good stuff can be done if feed readers are willing to delegate list management to other services. IMHO the only reason a reader developer wouldn't do it is because they want to lock users in. If I let you edit your feed list elsewhere that means you could give the list to another vendor and have a choice which to use. It's really something users should demand, esp if you're paying for the service.

\n", - "pubDate": "Mon, 26 Jun 2017 12:24:36 GMT", - "guid": "http://scripting.com/2017/06/26.html#a080636", - "source:outline": { - "text": "Subscribable feed lists give power to users", - "created": "Mon, 26 Jun 2017 12:24:36 GMT", - "type": "outline", - "subs": [ - { - "text": "An interesting comment from Chris Aldrich about subscribing to lists of feeds in a thread on the Woodwind app site on GitHub. ", - "created": "Mon, 26 Jun 2017 12:24:49 GMT", - "image": "http://static.scripting.com/larryKing/images/2014/05/25/goodHumor.gif", - "permalink": "http://scripting.com/2017/06/26.html#a080649" - }, - { - "text": "Here's the basic idea. There's a difference between importing OPML into a reader and subscribing to it. The latter is very powerful, for the user, but a lot of RSS reader devs may not want their users to have that much power. It's not a very hard feature to implement. ", - "created": "Mon, 26 Jun 2017 12:40:32 GMT", - "permalink": "http://scripting.com/2017/06/26.html#a080632" - }, - { - "text": "The idea has been much-discussed here. We call them reading lists. Michael Arrington even wrote a TechCrunch piece about it in 2005. ", - "created": "Mon, 26 Jun 2017 12:26:01 GMT", - "permalink": "http://scripting.com/2017/06/26.html#a080601" - }, - { - "text": "Subscribable OPML is something all my readers have been able to do through an OPML feature called inclusion. I wrote a howto for a \"River5\" user re inclusion just last week.", - "created": "Mon, 26 Jun 2017 12:27:15 GMT", - "permalink": "http://scripting.com/2017/06/26.html#a080615" - }, - { - "text": "Share Your OPML was a service I operated for a while. It made it possible to manage your OPML separate from the reader you used. It was meant to encourage readers to support subscribable OPML. I'm looking for an excuse to bring it back, but first we need a base of shared feed lists. ", - "created": "Mon, 26 Jun 2017 12:33:10 GMT", - "permalink": "http://scripting.com/2017/06/26.html#a080610" - }, - { - "text": "A lot of good stuff can be done if feed readers are willing to delegate list management to other services. IMHO the only reason a reader developer wouldn't do it is because they want to lock users in. If I let you edit your feed list elsewhere that means you could give the list to another vendor and have a choice which to use. It's really something users should demand, esp if you're paying for the service. ", - "created": "Mon, 26 Jun 2017 12:47:13 GMT", - "permalink": "http://scripting.com/2017/06/26.html#a080613" - } - ], - "permalink": "http://scripting.com/2017/06/26.html#a080636" - } - }, - { - "link": "http://scripting.com/2017/06/25.html#a080631", - "description": "Good morning Internet guzzlers! 🍺", - "pubDate": "Sun, 25 Jun 2017 12:27:31 GMT", - "guid": "http://scripting.com/2017/06/25.html#a080631", - "source:outline": { - "text": "Good morning Internet guzzlers! :beer:", - "created": "Sun, 25 Jun 2017 12:27:31 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/25.html#a080631" - } - }, - { - "link": "http://scripting.com/2017/06/25.html#a080606", - "description": "So glad I stopped worrying about Facebook and am now blogging Old School style on scripting.com. I've found my sea legs once again. ", - "pubDate": "Sun, 25 Jun 2017 12:43:06 GMT", - "guid": "http://scripting.com/2017/06/25.html#a080606", - "source:outline": { - "text": "So glad I stopped worrying about Facebook and am now blogging Old School style on scripting.com. I've found my sea legs once again. ", - "created": "Sun, 25 Jun 2017 12:43:06 GMT", - "type": "tweet", - "tweetId": "878956761302147072", - "tweetUserName": "davewiner", - "permalink": "http://scripting.com/2017/06/25.html#a080606" - } - }, - { - "link": "http://scripting.com/2017/06/25.html#a080623", - "description": "Dan Shafer died. I knew Dan from the Mac developer community in the 80s, hired him to write the first docs for Frontier. Bon voyage mi amigo! 💥", - "pubDate": "Sun, 25 Jun 2017 12:56:23 GMT", - "guid": "http://scripting.com/2017/06/25.html#a080623", - "source:outline": { - "text": "Dan Shafer died. I knew Dan from the Mac developer community in the 80s, hired him to write the first docs for \"Frontier\". Bon voyage mi amigo! :boom:", - "created": "Sun, 25 Jun 2017 12:56:23 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/25.html#a080623" - } - }, - { - "link": "http://scripting.com/2017/06/25.html#a080624", - "description": "Money was a big issue yesterday in the nascent tech blogosphere. First, you do this because you love it, not because it pays well. (It doesn't pay at all.) Now I'd like to take you back to a discsussion that was had many years ago that resulted in this conclusion. You don't make money from this work, but it leads to opportunities where you can make money. Ideas and information make their way to you and if you are so-inclined you can make money by investing in those ideas. No sure things, but some bloggers have made billions, and others have made millions. And others have made a decent living. Not from their blog but because they blog. ", - "pubDate": "Sun, 25 Jun 2017 12:29:24 GMT", - "guid": "http://scripting.com/2017/06/25.html#a080624", - "source:outline": { - "text": "Money was a big issue yesterday in the nascent tech blogosphere. First, you do this because you love it, not because it pays well. (It doesn't pay at all.) Now I'd like to take you back to a discsussion that was had many years ago that resulted in this conclusion. You don't make money from this work, but it leads to opportunities where you can make money. Ideas and information make their way to you and if you are so-inclined you can make money by investing in those ideas. No sure things, but some bloggers have made billions, and others have made millions. And others have made a decent living. Not from their blog but because they blog. ", - "created": "Sun, 25 Jun 2017 12:29:24 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/25.html#a080624" - } - }, - { - "link": "http://scripting.com/2017/06/25.html#a080631", - "description": "In 2015 I wrote that Dropbox could be the king of the one-page app. Because storage is the thing the web doesn't, on its own, do, and storage is the thing Dropbox does best. And they have an API, and they understood the connection to one-page-apps earlier than anyone. But it didn't happen. I've emailed with people at Dropbox from time to time and the best explanation I can come up with is that they are focused in different areas. It seems to me, from my outside perch, that they are trying to become a competitor to Google's and Microsoft's Office products. I was hoping they'd become a platform, focusing on distribution and investment in startups.", - "pubDate": "Sun, 25 Jun 2017 12:32:31 GMT", - "guid": "http://scripting.com/2017/06/25.html#a080631", - "source:outline": { - "text": "In 2015 I wrote that Dropbox could be the king of the one-page app. Because storage is the thing the web doesn't, on its own, do, and storage is the thing Dropbox does best. And they have an API, and they understood the connection to one-page-apps earlier than anyone. But it didn't happen. I've emailed with people at Dropbox from time to time and the best explanation I can come up with is that they are focused in different areas. It seems to me, from my outside perch, that they are trying to become a competitor to Google's and Microsoft's Office products. I was hoping they'd become a platform, focusing on distribution and investment in startups.", - "created": "Sun, 25 Jun 2017 12:32:31 GMT", - "type": "outline", - "image": "http://radio3.io/icons/clarus.gif", - "permalink": "http://scripting.com/2017/06/25.html#a080631" - } - }, - { - "title": "Code mode is for real", - "link": "http://scripting.com/2017/06/25.html#a110613", - "description": "

\"Code

\n", - "pubDate": "Sun, 25 Jun 2017 15:53:13 GMT", - "guid": "http://scripting.com/2017/06/25.html#a110613", - "source:outline": { - "text": "Code mode is for real", - "created": "Sun, 25 Jun 2017 15:53:13 GMT", - "type": "outline", - "subs": [ - { - "text": "\"Code", - "created": "Sun, 25 Jun 2017 15:53:18 GMT", - "permalink": "http://scripting.com/2017/06/25.html#a110618" - } - ], - "permalink": "http://scripting.com/2017/06/25.html#a110613" - } - }, - { - "link": "http://scripting.com/2017/06/24.html#a100601", - "description": "Good morning sports fans! 🏈", - "pubDate": "Sat, 24 Jun 2017 14:57:01 GMT", - "guid": "http://scripting.com/2017/06/24.html#a100601", - "source:outline": { - "text": "Good morning sports fans! :football:", - "created": "Sat, 24 Jun 2017 14:57:01 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/24.html#a100601" - } - }, - { - "link": "http://scripting.com/2017/06/24.html#a040606", - "description": "Health care is socialist is getting a bunch of new reads today thanks to some powerful RTs.", - "pubDate": "Sat, 24 Jun 2017 20:50:06 GMT", - "guid": "http://scripting.com/2017/06/24.html#a040606", - "source:outline": { - "text": "Health care is socialist is getting a bunch of new reads today thanks to some powerful RTs.", - "created": "Sat, 24 Jun 2017 20:50:06 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/24.html#a040606" - } - }, - { - "link": "http://scripting.com/2017/06/24.html#a020644", - "description": "Today is emoji day ⭐️ on Scripting News. We have all the ⭐️ best emoji. And they're free, for you, the best ⭐️ people in the universe, the readers of ⭐️ this humble blog. ⭐️ ", - "pubDate": "Sat, 24 Jun 2017 18:34:44 GMT", - "guid": "http://scripting.com/2017/06/24.html#a020644", - "source:outline": { - "text": "Today is emoji day :star: on Scripting News. We have all the :star: best emoji. And they're free, for you, the best :star: people in the universe, the readers of :star: this humble blog. :star: ", - "created": "Sat, 24 Jun 2017 18:34:44 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/24.html#a020644" - } - }, - { - "link": "http://scripting.com/2017/06/24.html#a020620", - "description": "It makes sense that because of reconcilliation Repubs have a hard time repealing ObamaCare. It seems fair that you should have to have 60 votes to repeal something that required 60 votes to pass. How will they explain it to the voters they've been lying to about \"repeal and replace.\" Double-talk. Swamp-talk. 👍", - "pubDate": "Sat, 24 Jun 2017 18:20:20 GMT", - "guid": "http://scripting.com/2017/06/24.html#a020620", - "source:outline": { - "text": "It makes sense that because of reconcilliation Repubs have a hard time repealing ObamaCare. It seems fair that you should have to have 60 votes to repeal something that required 60 votes to pass. How will they explain it to the voters they've been lying to about \"repeal and replace.\" Double-talk. Swamp-talk. :+1:", - "created": "Sat, 24 Jun 2017 18:20:20 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/24.html#a020620" - } - }, - { - "link": "http://scripting.com/2017/06/24.html#a020649", - "description": "I've started a chatroom on Gitter. Not sure what I'll use it for. Also not sure if it's open for anyone to join. I want it to be. The community guidelines apply. Keep it short, respectful and on-topic, and no spam. ⚾️", - "pubDate": "Sat, 24 Jun 2017 18:05:49 GMT", - "guid": "http://scripting.com/2017/06/24.html#a020649", - "source:outline": { - "text": "I've started a chatroom on Gitter. Not sure what I'll use it for. Also not sure if it's open for anyone to join. I want it to be. The community guidelines apply. Keep it short, respectful and on-topic, and no spam. :baseball:", - "created": "Sat, 24 Jun 2017 18:05:49 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/24.html#a020649" - } - }, - { - "link": "http://scripting.com/2017/06/24.html#a110617", - "description": "BTW, don't ask me to pitch RSS, I won't do it, because the request is based on a misunderstanding. RSS is not a product, it's a format. I have never made a dime from it. You have as much at stake in its success as I do. So I always turn it around and ask the person who asked me for a pitch to instead pitch me on it. I don't budge on this. Ever. 🏀", - "pubDate": "Sat, 24 Jun 2017 15:19:17 GMT", - "guid": "http://scripting.com/2017/06/24.html#a110617", - "source:outline": { - "text": "BTW, don't ask me to pitch RSS, I won't do it, because the request is based on a misunderstanding. RSS is not a product, it's a format. I have never made a dime from it. You have as much at stake in its success as I do. So I always turn it around and ask the person who asked me for a pitch to instead pitch me on it. I don't budge on this. Ever. :basketball:", - "created": "Sat, 24 Jun 2017 15:19:17 GMT", - "type": "outline", - "image": "http://scripting.com/images/2017/06/24/rssTShirt.png", - "permalink": "http://scripting.com/2017/06/24.html#a110617" - } - }, - { - "link": "http://scripting.com/2017/06/24.html#a110610", - "description": "Is anyone using the JSON version of the Scripting News feed in their feed reader? It's a bit of a trick question, in a way, because as far as I know, only River5 and Electric River support this format. If so, send me an email at my address, on the About page here. 🍰", - "pubDate": "Sat, 24 Jun 2017 15:01:10 GMT", - "guid": "http://scripting.com/2017/06/24.html#a110610", - "source:outline": { - "text": "Is anyone using the JSON version of the Scripting News feed in their feed reader? It's a bit of a trick question, in a way, because as far as I know, only \"River5\" and \"Electric River\" support this format. If so, send me an email at my address, on the About page here. :cake:", - "created": "Sat, 24 Jun 2017 15:01:10 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/24.html#a110610" - } - }, - { - "link": "http://scripting.com/2017/06/24.html#a010650", - "description": "Journalists keep making a serious technology error -- assuming the only damage Russia can do to our government is during elections. 🇺🇸 ", - "pubDate": "Sat, 24 Jun 2017 05:50:50 GMT", - "guid": "http://scripting.com/2017/06/24.html#a010650", - "source:outline": { - "text": "Journalists keep making a serious technology error -- assuming the only damage Russia can do to our government is during elections. :us: ", - "created": "Sat, 24 Jun 2017 05:50:50 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/24.html#a010650" - } - }, - { - "link": "http://scripting.com/2017/06/24.html#a100632", - "description": "I get my health insurance through the ACA. It's very much alive, Spicer. Without it I would not have health insurance. 🍋 ", - "pubDate": "Sat, 24 Jun 2017 14:26:32 GMT", - "guid": "http://scripting.com/2017/06/24.html#a100632", - "source:outline": { - "text": "I get my health insurance through the ACA. It's very much alive, Spicer. Without it I would not have health insurance. :lemon: ", - "created": "Sat, 24 Jun 2017 14:26:32 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/24.html#a100632" - } - }, - { - "title": "The sad state of tech news in 2017", - "link": "http://scripting.com/2017/06/24.html#a090648", - "description": "

It won't take much to reboot the tech blogosphere, just a few bloggers with ideas who listen to each other and want to work with each other. That was the idea behind blogrolls, to visibly show the relationships.

\n
    \n\t
  • I'm still thinking about how to integrate a blogroll with the new design of Scripting News. I pushed everything aside to get a clean look for the new site. I want to avoid bringing it all back.
  • \n\t
\n

So, we have a few people who are writing and listening. That's a needed first step. Next we need a way to announce and hear about new tech products. Not just ones that get VC backing or come from big companies. We already hear about those products through TechMeme and the pubs that contribute to it. We also have platform-specific news about tech products, it's more limited, but it's there.

\n

It will likely start with word of mouth among the bloggers. If Richard is using a product and speaks highly of it, I'm likely to take a look. Especially if he says it fits into what I'm doing through the open formats my software already supports.

\n

Then I want a river, a place where I can go to find out quickly what's new, in the way of products, not BigCo bluster or another $250 million VC deal. I want to know what my peers are doing. So I can learn from them, and so we can make our products work with theirs.

\n

Work together is a phrase you'll hear me use a lot. It's the potential of tech, but it often isn't the attitude of tech. Even the smallest most independent developers dream of dominating. You can't work with people who dominate, even if they win.

\n

I want to hear about products that are open to connecting to mine.

\n

There have been times, often defined by news sources, that have created huge swells of compatible technology. To name a few: InfoWorld, PC WEEK, MacWEEK, TechCrunch. Very fond memories of the communities that gathered around each of those.

\n

It's time for another. The opportunity is there. It's been a long time since we had an open development community that worked to create great new user experience without lockin. It's like riding a bicycle or swimming, you don't forget how to do it. And like tennis or baseball, you can't play without partners and competition.

\n", - "pubDate": "Sat, 24 Jun 2017 13:43:48 GMT", - "guid": "http://scripting.com/2017/06/24.html#a090648", - "source:outline": { - "text": "The sad state of tech news in 2017", - "created": "Sat, 24 Jun 2017 13:43:48 GMT", - "type": "outline", - "subs": [ - { - "text": "It won't take much to reboot the tech blogosphere, just a few bloggers with ideas who listen to each other and want to work with each other. That was the idea behind blogrolls, to visibly show the relationships. ", - "created": "Sat, 24 Jun 2017 13:43:55 GMT", - "subs": [ - { - "text": "I'm still thinking about how to integrate a blogroll with the new design of \"Scripting News\". I pushed everything aside to get a clean look for the new site. I want to avoid bringing it all back. ", - "created": "Sat, 24 Jun 2017 14:32:17 GMT", - "permalink": "http://scripting.com/2017/06/24.html#a100617" - } - ], - "permalink": "http://scripting.com/2017/06/24.html#a090655" - }, - { - "text": "So, we have a few people who are writing and listening. That's a needed first step. Next we need a way to announce and hear about new tech products. Not just ones that get VC backing or come from big companies. We already hear about those products through \"TechMeme\" and the pubs that contribute to it. We also have platform-specific news about tech products, it's more limited, but it's there.", - "created": "Sat, 24 Jun 2017 13:44:09 GMT", - "permalink": "http://scripting.com/2017/06/24.html#a090609" - }, - { - "text": "It will likely start with word of mouth among the bloggers. If Richard is using a product and speaks highly of it, I'm likely to take a look. Especially if he says it fits into what I'm doing through the open formats my software already supports. ", - "created": "Sat, 24 Jun 2017 13:52:04 GMT", - "permalink": "http://scripting.com/2017/06/24.html#a090604" - }, - { - "text": "Then I want a river, a place where I can go to find out quickly what's new, in the way of products, not BigCo bluster or another $250 million VC deal. I want to know what my peers are doing. So I can learn from them, and so we can make our products work with theirs. ", - "created": "Sat, 24 Jun 2017 13:52:04 GMT", - "permalink": "http://scripting.com/2017/06/24.html#a090604" - }, - { - "text": "Work together is a phrase you'll hear me use a lot. It's the potential of tech, but it often isn't the attitude of tech. Even the smallest most independent developers dream of dominating. You can't work with people who dominate, even if they win. ", - "created": "Sat, 24 Jun 2017 13:54:33 GMT", - "permalink": "http://scripting.com/2017/06/24.html#a090633" - }, - { - "text": "I want to hear about products that are open to connecting to mine. ", - "created": "Sat, 24 Jun 2017 20:53:41 GMT", - "permalink": "http://scripting.com/2017/06/24.html#a040641" - }, - { - "text": "There have been times, often defined by news sources, that have created huge swells of compatible technology. To name a few: InfoWorld, PC WEEK, MacWEEK, TechCrunch. Very fond memories of the communities that gathered around each of those. ", - "created": "Sat, 24 Jun 2017 13:48:02 GMT", - "permalink": "http://scripting.com/2017/06/24.html#a090602" - }, - { - "text": "It's time for another. The opportunity is there. It's been a long time since we had an open development community that worked to create great new user experience without lockin. It's like riding a bicycle or swimming, you don't forget how to do it. And like tennis or baseball, you can't play without partners and competition. ", - "created": "Sat, 24 Jun 2017 13:57:02 GMT", - "permalink": "http://scripting.com/2017/06/24.html#a090602" - } - ], - "permalink": "http://scripting.com/2017/06/24.html#a090648" - } - }, - { - "title": "Republican-inspired art", - "link": "http://scripting.com/2017/06/24.html#a100632", - "description": "

\"A

\n", - "pubDate": "Sat, 24 Jun 2017 14:52:32 GMT", - "guid": "http://scripting.com/2017/06/24.html#a100632", - "source:outline": { - "text": "Republican-inspired art", - "created": "Sat, 24 Jun 2017 14:52:32 GMT", - "type": "outline", - "subs": [ - { - "text": "\"A", - "created": "Sat, 24 Jun 2017 14:52:37 GMT", - "permalink": "http://scripting.com/2017/06/24.html#a100637" - } - ], - "permalink": "http://scripting.com/2017/06/24.html#a100632" - } - }, - { - "link": "http://scripting.com/2017/06/23.html#a070648", - "description": "Thank you Om for the tweet-love. ❤️ ", - "pubDate": "Fri, 23 Jun 2017 11:50:48 GMT", - "guid": "http://scripting.com/2017/06/23.html#a070648", - "source:outline": { - "text": "Thank you Om for the tweet-love. :heart: ", - "created": "Fri, 23 Jun 2017 11:50:48 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/23.html#a070648" - } - }, - { - "link": "http://scripting.com/2017/06/23.html#a120634", - "description": "An idea worth RT'ing: \"A site people could go to, fill in some info about themselves, and find out how much they would lose under the Repub plan.\"", - "pubDate": "Fri, 23 Jun 2017 16:02:34 GMT", - "guid": "http://scripting.com/2017/06/23.html#a120634", - "source:outline": { - "text": "An idea worth RT'ing: \"A site people could go to, fill in some info about themselves, and find out how much they would lose under the Repub plan.\"", - "created": "Fri, 23 Jun 2017 16:02:34 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/23.html#a120634" - } - }, - { - "link": "http://scripting.com/2017/06/23.html#a090638", - "description": "githubpub is a Node app that serves from GitHub repositories.", - "pubDate": "Fri, 23 Jun 2017 13:52:38 GMT", - "guid": "http://scripting.com/2017/06/23.html#a090638", - "source:outline": { - "text": "\"githubpub\" is a Node app that serves from GitHub repositories.", - "created": "Fri, 23 Jun 2017 13:52:38 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/23.html#a090638" - } - }, - { - "link": "http://scripting.com/2017/06/23.html#a100602", - "description": "Fix for the Scripting News RSS feed: we now process glossary entries and emoji short codes. The net effect is that text shortcuts like RSS will be expanded as well as 👏 emoji 👏, in the feeds. As they say, still diggin!", - "pubDate": "Fri, 23 Jun 2017 14:52:02 GMT", - "guid": "http://scripting.com/2017/06/23.html#a100602", - "source:outline": { - "text": "Fix for the Scripting News RSS feed: we now process glossary entries and emoji short codes. The net effect is that text shortcuts like \"rss\" will be expanded as well as :clap: emoji :clap:, in the feeds. As they say, still diggin!", - "created": "Fri, 23 Jun 2017 14:52:02 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/23.html#a100602" - } - }, - { - "link": "http://scripting.com/2017/06/23.html#a070659", - "description": "So many thoughts today begin with -- If the Dems only had their shit together. ", - "pubDate": "Fri, 23 Jun 2017 11:52:59 GMT", - "guid": "http://scripting.com/2017/06/23.html#a070659", - "source:outline": { - "text": "So many thoughts today begin with -- If the Dems only had their shit together. ", - "created": "Fri, 23 Jun 2017 11:52:59 GMT", - "type": "tweet", - "tweetId": "878220762343133184", - "tweetUserName": "davewiner", - "permalink": "http://scripting.com/2017/06/23.html#a070659" - } - }, - { - "link": "http://scripting.com/2017/06/23.html#a070624", - "description": "If the Dems only had their shit together, we would be mobleizable to knock on neighbor's doors this weekend with pre-written talking points. \"Did you know that you could will lose your health insurance if the Republicans have their way?\"", - "pubDate": "Fri, 23 Jun 2017 11:53:24 GMT", - "guid": "http://scripting.com/2017/06/23.html#a070624", - "source:outline": { - "text": "If the Dems only had their shit together, we would be mobleizable to knock on neighbor's doors this weekend with pre-written talking points. \"Did you know that you could will lose your health insurance if the Republicans have their way?\"", - "created": "Fri, 23 Jun 2017 11:53:24 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/23.html#a070624" - } - }, - { - "link": "http://scripting.com/2017/06/23.html#a070633", - "description": "Repubs who consider voting for the repeal of Medicaid and the ACA should fear the hellfire they will face when they run for re-election. This weekend is the time to make your feelings felt. ", - "pubDate": "Fri, 23 Jun 2017 11:47:33 GMT", - "guid": "http://scripting.com/2017/06/23.html#a070633", - "source:outline": { - "text": "Repubs who consider voting for the repeal of Medicaid and the ACA should fear the hellfire they will face when they run for re-election. This weekend is the time to make your feelings felt. ", - "created": "Fri, 23 Jun 2017 11:47:33 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/23.html#a070633" - } - }, - { - "link": "http://scripting.com/2017/06/23.html#a070641", - "description": "BTW, if someone expresses frustration it doesn't follow that they blame you, esp on Twitter which is a length-impaired medium. ", - "pubDate": "Fri, 23 Jun 2017 11:45:41 GMT", - "guid": "http://scripting.com/2017/06/23.html#a070641", - "source:outline": { - "text": "BTW, if someone expresses frustration it doesn't follow that they blame you, esp on Twitter which is a length-impaired medium. ", - "created": "Fri, 23 Jun 2017 11:45:41 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/23.html#a070641" - } - }, - { - "title": "Thin servers", - "link": "http://scripting.com/2017/06/23.html#a100620", - "description": "

Two friends, Jon Udell and Mike Caulfield, are talking about \"dumb\" servers. I call the same things \"thin.\" Also fractional-horsepower servers. They go by a bunch of names, but the idea and motivation is the same.

\n

The idea: We move functionality from the server to the edge (desktop, mobile device), repeating until someday there's nothing left on the server. We could go all the way, but it needs a strong operational backend, something a big company is good at, not so much individuals. (With the caveat that some think this problem is distributable, notably the fictional CEO of Pied Piper.)

\n

The key thing is identity. Once you have that solved, it all becomes relatively easy. I've factored out identity into a layer I call nodeStorage. It associates storage with a user's Twitter identity. Twitter is a good service to use, unlike some others, because they have a liberal policy of who gets to create apps. Faceook has an extensive vetting process. Twitter is \"let a thousand flowers bloom.\" I know some people have problems with Twitter, but I've learned over many years that all corporate vendors are imperfect. If you're waiting for perfection you'll wait forever. And you build the software so that if Twitter should again become draconian, a new service can be filled in with as little disruption as possible.

\n

Another place I've looked is Dropbox. There, with one simple feature, the ability to associate a domain with a folder, they would solve the problem. I know there are external services that provide something \"like\" this, but fundamentally Dropbox doesn't provide enough flexibility in the API to do this in a reasonable way. (Lack of granularity in permissions, an app gets access to one folder or everything.)

\n

Or Amazon, if their identity system for AWS were simpler for end users, or if their end-user storage system could be accessed through the S3 API. I'm sure they've thought of it. There must be a reason they don't do it.

\n

And Twitter could completely eliminate the need for nodeStorage, by offering users a few gigabytes of storage attached to their Twitter account, accessible through the API. The first person who described the feature to me was Jack Dorsey, about eight years ago, when we met for coffee in SF. So he understands why this idea is so powerful. I'm not sure what the holdup is.

\n

In the meantime, nodeStorage works. I build the kind of apps I want. Open the sidebar on Scripting News (left margin) and have a look at the apps. Some even have source code so you can see for yourself.

\n", - "pubDate": "Fri, 23 Jun 2017 14:03:20 GMT", - "guid": "http://scripting.com/2017/06/23.html#a100620", - "source:outline": { - "text": "Thin servers", - "created": "Fri, 23 Jun 2017 14:03:20 GMT", - "type": "outline", - "subs": [ - { - "text": "Two friends, Jon Udell and Mike Caulfield, are talking about \"dumb\" servers. I call the same things \"thin.\" Also fractional-horsepower servers. They go by a bunch of names, but the idea and motivation is the same. ", - "created": "Fri, 23 Jun 2017 14:05:30 GMT", - "permalink": "http://scripting.com/2017/06/23.html#a100630" - }, - { - "text": "The idea: We move functionality from the server to the edge (desktop, mobile device), repeating until someday there's nothing left on the server. We could go all the way, but it needs a strong operational backend, something a big company is good at, not so much individuals. (With the caveat that some think this problem is distributable, notably the fictional CEO of Pied Piper.)", - "created": "Fri, 23 Jun 2017 14:24:35 GMT", - "permalink": "http://scripting.com/2017/06/23.html#a100635" - }, - { - "text": "The key thing is identity. Once you have that solved, it all becomes relatively easy. I've factored out identity into a layer I call \"nodeStorage\". It associates storage with a user's Twitter identity. Twitter is a good service to use, unlike some others, because they have a liberal policy of who gets to create apps. Faceook has an extensive vetting process. Twitter is \"let a thousand flowers bloom.\" I know some people have problems with Twitter, but I've learned over many years that all corporate vendors are imperfect. If you're waiting for perfection you'll wait forever. And you build the software so that if Twitter should again become draconian, a new service can be filled in with as little disruption as possible.", - "created": "Fri, 23 Jun 2017 14:05:43 GMT", - "image": "http://scripting.com/images/2017/06/17/bowling.png", - "permalink": "http://scripting.com/2017/06/23.html#a100643" - }, - { - "text": "Another place I've looked is Dropbox. There, with one simple feature, the ability to associate a domain with a folder, they would solve the problem. I know there are external services that provide something \"like\" this, but fundamentally Dropbox doesn't provide enough flexibility in the API to do this in a reasonable way. (Lack of granularity in permissions, an app gets access to one folder or everything.)", - "created": "Fri, 23 Jun 2017 14:08:41 GMT", - "permalink": "http://scripting.com/2017/06/23.html#a100641" - }, - { - "text": "Or Amazon, if their identity system for AWS were simpler for end users, or if their end-user storage system could be accessed through the S3 API. I'm sure they've thought of it. There must be a reason they don't do it. ", - "created": "Fri, 23 Jun 2017 14:09:10 GMT", - "permalink": "http://scripting.com/2017/06/23.html#a100610" - }, - { - "text": "And Twitter could completely eliminate the need for nodeStorage, by offering users a few gigabytes of storage attached to their Twitter account, accessible through the API. The first person who described the feature to me was Jack Dorsey, about eight years ago, when we met for coffee in SF. So he understands why this idea is so powerful. I'm not sure what the holdup is. ", - "created": "Fri, 23 Jun 2017 14:09:55 GMT", - "permalink": "http://scripting.com/2017/06/23.html#a100655" - }, - { - "text": "In the meantime, \"nodeStorage\" works. I build the kind of apps I want. Open the sidebar on Scripting News (left margin) and have a look at the apps. Some even have source code so you can see for yourself. ", - "created": "Fri, 23 Jun 2017 14:33:10 GMT", - "permalink": "http://scripting.com/2017/06/23.html#a100610" - } - ], - "permalink": "http://scripting.com/2017/06/23.html#a100620" - } - }, - { - "title": "RicMac, part II", - "link": "http://scripting.com/2017/06/23.html#a070643", - "description": "

Richard MacManus keeps on truckin. There's nothing more powerful than a persistent and curious user who's relatively fearless.

\n

In a follow-up post I learned that there is an IndieWeb-approved feed reader called Woodwind. That's good news. RSS and related technolgies, including OPML import and export, are essential components of the open web.

\n

BTW, to Richard, I wrote up my rules for standards-makers, based on experience re what (imho) is important and what works and doesn't. Another item for your consideration.

\n", - "pubDate": "Fri, 23 Jun 2017 11:14:43 GMT", - "guid": "http://scripting.com/2017/06/23.html#a070643", - "source:outline": { - "text": "RicMac, part II", - "created": "Fri, 23 Jun 2017 11:14:43 GMT", - "type": "outline", - "subs": [ - { - "text": "Richard MacManus keeps on truckin. There's nothing more powerful than a persistent and curious user who's relatively fearless. ", - "created": "Fri, 23 Jun 2017 11:20:38 GMT", - "image": "http://scripting.com/images/2017/06/23/mrNatural.png", - "permalink": "http://scripting.com/2017/06/23.html#a070638" - }, - { - "text": "In a follow-up post I learned that there is an IndieWeb-approved feed reader called Woodwind. That's good news. \"RSS\" and related technolgies, including OPML import and export, are essential components of the open web. ", - "created": "Fri, 23 Jun 2017 11:15:54 GMT", - "permalink": "http://scripting.com/2017/06/23.html#a070654" - }, - { - "text": "BTW, to Richard, I wrote up my rules for standards-makers, based on experience re what (imho) is important and what works and doesn't. Another item for your consideration. ", - "created": "Fri, 23 Jun 2017 11:19:38 GMT", - "permalink": "http://scripting.com/2017/06/23.html#a070638" - } - ], - "permalink": "http://scripting.com/2017/06/23.html#a070643" - } - }, - { - "title": "Test post", - "link": "http://scripting.com/2017/06/23.html#a030621", - "description": "

Here's a list with four items

\n
    \n\t
  • one
  • \n\t
  • two
  • \n\t
  • three
  • \n\t
  • four
  • \n\t
\n", - "pubDate": "Fri, 23 Jun 2017 19:48:21 GMT", - "guid": "http://scripting.com/2017/06/23.html#a030621", - "source:outline": { - "text": "Test post", - "created": "Fri, 23 Jun 2017 19:48:21 GMT", - "type": "outline", - "subs": [ - { - "text": "Here's a list with four items", - "created": "Fri, 23 Jun 2017 19:49:21 GMT", - "flNumberedSubs": "true", - "subs": [ - { - "text": "one", - "created": "Fri, 23 Jun 2017 19:48:43 GMT", - "permalink": "http://scripting.com/2017/06/23.html#a030643" - }, - { - "text": "two", - "created": "Fri, 23 Jun 2017 19:48:44 GMT", - "permalink": "http://scripting.com/2017/06/23.html#a030644" - }, - { - "text": "three", - "created": "Fri, 23 Jun 2017 19:48:45 GMT", - "permalink": "http://scripting.com/2017/06/23.html#a030645" - }, - { - "text": "four", - "created": "Fri, 23 Jun 2017 19:48:46 GMT", - "permalink": "http://scripting.com/2017/06/23.html#a030646" - } - ], - "permalink": "http://scripting.com/2017/06/23.html#a030621" - } - ], - "permalink": "http://scripting.com/2017/06/23.html#a030621" - } - }, - { - "link": "http://scripting.com/2017/06/22.html#a120633", - "description": "I'm working on a new Node web server that serves out of GitHub repos. It's a very sweet very small piece of software. ", - "pubDate": "Thu, 22 Jun 2017 16:28:33 GMT", - "guid": "http://scripting.com/2017/06/22.html#a120633", - "source:outline": { - "text": "I'm working on a new Node web server that serves out of GitHub repos. It's a very sweet very small piece of software. ", - "created": "Thu, 22 Jun 2017 16:28:33 GMT", - "type": "tweet", - "tweetId": "877926320822464512", - "tweetUserName": "davewiner", - "permalink": "http://scripting.com/2017/06/22.html#a120633" - } - }, - { - "link": "http://scripting.com/2017/06/22.html#a090616", - "description": "An epiphany. Mark Zuckerberg is his generation's Ray Kroc, and Facebook is McDonald's. I aspire to be Alice Waters.", - "pubDate": "Thu, 22 Jun 2017 13:29:16 GMT", - "guid": "http://scripting.com/2017/06/22.html#a090616", - "source:outline": { - "text": "An epiphany. Mark Zuckerberg is his generation's Ray Kroc, and Facebook is McDonald's. I aspire to be Alice Waters.", - "created": "Thu, 22 Jun 2017 13:29:16 GMT", - "type": "tweet", - "tweetId": "877881227298000896", - "tweetUserName": "davewiner", - "permalink": "http://scripting.com/2017/06/22.html#a090616" - } - }, - { - "link": "http://scripting.com/2017/06/22.html#a090644", - "description": "Future-of-journalism conferences that ignore blogging are not about the future of journalism.", - "pubDate": "Thu, 22 Jun 2017 13:29:44 GMT", - "guid": "http://scripting.com/2017/06/22.html#a090644", - "source:outline": { - "text": "Future-of-journalism conferences that ignore blogging are not about the future of journalism.", - "created": "Thu, 22 Jun 2017 13:29:44 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/22.html#a090644" - } - }, - { - "link": "http://scripting.com/2017/06/22.html#a120629", - "description": "News will be interesting tonight. They've got the Repub health care bill to rip apart, and it's also NBA Draft night. ", - "pubDate": "Thu, 22 Jun 2017 16:27:29 GMT", - "guid": "http://scripting.com/2017/06/22.html#a120629", - "source:outline": { - "text": "News will be interesting tonight. They've got the Repub health care bill to rip apart, and it's also NBA Draft night. ", - "created": "Thu, 22 Jun 2017 16:27:29 GMT", - "type": "tweet", - "tweetId": "877926057260728320", - "tweetUserName": "davewiner", - "permalink": "http://scripting.com/2017/06/22.html#a120629" - } - }, - { - "title": "Fargo puzzler", - "link": "http://scripting.com/2017/06/22.html#a030610", - "description": "

The last two episodes of season 3 of Fargo were fantastic. But, the opening scene of episode 1, which takes place in a police office in East Germany during the Cold War, is without explanation.

\n

All through the season, I was wondering how it was going to be connected up with the story that takes place in Minnesota in 2011, but as far as I know it never was.

\n

Maybe that was VM Varga as the accused? Or the police guy?

\n

This is kind of bothering me! :-)

\n

Okay then...

\n

Update: In the episode guide on Wikipedia they describe the opening scene as follows: \"In 1988 East Berlin, Jacob Ungerleider is questioned in the death of a woman, which he claims is a case of mistaken identity.\" So it's not VM Varga in the hot seat. Who is Jacob Ungerleider? I have no idea! ;-)

\n", - "pubDate": "Thu, 22 Jun 2017 19:26:10 GMT", - "guid": "http://scripting.com/2017/06/22.html#a030610", - "source:outline": { - "text": "Fargo puzzler", - "created": "Thu, 22 Jun 2017 19:26:10 GMT", - "type": "outline", - "subs": [ - { - "text": "The last two episodes of season 3 of Fargo were fantastic. But, the opening scene of episode 1, which takes place in a police office in East Germany during the Cold War, is without explanation. ", - "created": "Thu, 22 Jun 2017 19:26:18 GMT", - "image": "http://scripting.com/images/2017/06/22/paulBunyan.png", - "permalink": "http://scripting.com/2017/06/22.html#a030618" - }, - { - "text": "All through the season, I was wondering how it was going to be connected up with the story that takes place in Minnesota in 2011, but as far as I know it never was.", - "created": "Thu, 22 Jun 2017 19:27:20 GMT", - "permalink": "http://scripting.com/2017/06/22.html#a030620" - }, - { - "text": "Maybe that was VM Varga as the accused? Or the police guy?", - "created": "Thu, 22 Jun 2017 19:27:54 GMT", - "permalink": "http://scripting.com/2017/06/22.html#a030654" - }, - { - "text": "This is kind of bothering me! :-)", - "created": "Thu, 22 Jun 2017 19:28:15 GMT", - "permalink": "http://scripting.com/2017/06/22.html#a030615" - }, - { - "text": "Okay then...", - "created": "Thu, 22 Jun 2017 19:28:27 GMT", - "permalink": "http://scripting.com/2017/06/22.html#a030627" - }, - { - "text": "Update: In the episode guide on Wikipedia they describe the opening scene as follows: \"In 1988 East Berlin, Jacob Ungerleider is questioned in the death of a woman, which he claims is a case of mistaken identity.\" So it's not VM Varga in the hot seat. Who is Jacob Ungerleider? I have no idea! ;-)", - "created": "Thu, 22 Jun 2017 19:38:25 GMT", - "permalink": "http://scripting.com/2017/06/22.html#a030625" - } - ], - "permalink": "http://scripting.com/2017/06/22.html#a030610" - } - }, - { - "link": "http://scripting.com/2017/06/21.html#a060647", - "description": "Good morning summer solstice fans!", - "pubDate": "Wed, 21 Jun 2017 10:21:47 GMT", - "guid": "http://scripting.com/2017/06/21.html#a060647", - "source:outline": { - "text": "Good morning summer solstice fans!", - "created": "Wed, 21 Jun 2017 10:21:47 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/21.html#a060647" - } - }, - { - "link": "http://scripting.com/2017/06/21.html#a060611", - "description": "It was a boring NBA postseason, for the most. But the excitement of next season is already starting, with the draft tomorrow, and deal season in full swing. The place to find all the news is nbariver.com. It's one of many rivers maintained by my River5 installation. ", - "pubDate": "Wed, 21 Jun 2017 10:22:11 GMT", - "guid": "http://scripting.com/2017/06/21.html#a060611", - "source:outline": { - "text": "It was a boring NBA postseason, for the most. But the excitement of next season is already starting, with the draft tomorrow, and deal season in full swing. The place to find all the news is nbariver.com. It's one of many rivers maintained by my \"River5\" installation. ", - "created": "Wed, 21 Jun 2017 10:22:11 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/21.html#a060611" - } - }, - { - "link": "http://scripting.com/2017/06/21.html#a060632", - "description": "BTW, I hate the term \"eating the dogfood.\" As much as I love dogs, it says that our users are pets, not sentient human beings, our equals. It also says our software is dog food. I think as a kid, as an experiment, a few of us kids actually ate dog food. It's a vague memory, that must have some basis in reality. It makes me nauseous to think about it. And that's what I think about when I hear the term. Please, let's find another way of saying \"My software is good because I use it, and vice versa.\"", - "pubDate": "Wed, 21 Jun 2017 10:57:32 GMT", - "guid": "http://scripting.com/2017/06/21.html#a060632", - "source:outline": { - "text": "BTW, I hate the term \"eating the dogfood.\" As much as I love dogs, it says that our users are pets, not sentient human beings, our equals. It also says our software is dog food. I think as a kid, as an experiment, a few of us kids actually ate dog food. It's a vague memory, that must have some basis in reality. It makes me nauseous to think about it. And that's what I think about when I hear the term. Please, let's find another way of saying \"My software is good because I use it, and vice versa.\"", - "created": "Wed, 21 Jun 2017 10:57:32 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/21.html#a060632" - } - }, - { - "link": "http://scripting.com/2017/06/21.html#a070633", - "description": "Yesterday I posted a screen shot of one of my posts on Facebook, to accolades from friends on Facebook. I deleted the post. I won't be doing it again. Facebook is not a place for blog posts. Not as long as they disable linking, styles, titles and podcasts. If you want to help Facebook destroy the open web, go for it. But I will not participate in that awful adventure.", - "pubDate": "Wed, 21 Jun 2017 11:01:33 GMT", - "guid": "http://scripting.com/2017/06/21.html#a070633", - "source:outline": { - "text": "Yesterday I posted a screen shot of one of my posts on Facebook, to accolades from friends on Facebook. I deleted the post. I won't be doing it again. Facebook is not a place for blog posts. Not as long as they disable linking, styles, titles and podcasts. If you want to help Facebook destroy the open web, go for it. But I will not participate in that awful adventure.", - "created": "Wed, 21 Jun 2017 11:01:33 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/21.html#a070633" - } - }, - { - "link": "http://scripting.com/2017/06/21.html#a060600", - "description": "On Facebook you are who the algorithm says you are. ", - "pubDate": "Wed, 21 Jun 2017 10:53:00 GMT", - "guid": "http://scripting.com/2017/06/21.html#a060600", - "source:outline": { - "text": "On Facebook you are who the algorithm says you are. ", - "created": "Wed, 21 Jun 2017 10:53:00 GMT", - "type": "outline", - "permalink": "http://scripting.com/2017/06/21.html#a060600" - } - }, - { - "title": "GitHub API example app", - "link": "http://scripting.com/2017/06/21.html#a110614", - "description": "

A simple web app that travels through the River5 repository in my GitHub account, producing a directory that reflects the structure of the repo.

\n

I couldn't find sample code that does this simple thing. Now I won't have to hunt for it, and neither will you. ;-)

\n

Here's the source code.

\n", - "pubDate": "Wed, 21 Jun 2017 15:18:14 GMT", - "guid": "http://scripting.com/2017/06/21.html#a110614", - "source:outline": { - "text": "GitHub API example app", - "created": "Wed, 21 Jun 2017 15:18:14 GMT", - "type": "outline", - "subs": [ - { - "text": "A simple web app that travels through the River5 repository in my GitHub account, producing a directory that reflects the structure of the repo.", - "created": "Wed, 21 Jun 2017 15:18:24 GMT", - "permalink": "http://scripting.com/2017/06/21.html#a110624" - }, - { - "text": "I couldn't find sample code that does this simple thing. Now I won't have to hunt for it, and neither will you. ;-) ", - "created": "Wed, 21 Jun 2017 15:18:36 GMT", - "permalink": "http://scripting.com/2017/06/21.html#a110636" - }, - { - "text": "Here's the source code. ", - "created": "Wed, 21 Jun 2017 15:19:39 GMT", - "permalink": "http://scripting.com/2017/06/21.html#a110639" - } - ], - "permalink": "http://scripting.com/2017/06/21.html#a110614" - } - }, - { - "title": "Bike video from two years ago", - "link": "http://scripting.com/2017/06/21.html#a020637", - "description": "

\n", - "pubDate": "Wed, 21 Jun 2017 18:47:37 GMT", - "guid": "http://scripting.com/2017/06/21.html#a020637", - "source:outline": { - "text": "Bike video from two years ago", - "created": "Wed, 21 Jun 2017 18:47:37 GMT", - "type": "outline", - "subs": [ - { - "text": "", - "created": "Wed, 21 Jun 2017 18:47:50 GMT", - "permalink": "http://scripting.com/2017/06/21.html#a020650" - } - ], - "permalink": "http://scripting.com/2017/06/21.html#a020637" - } - }, - { - "title": "An old friend: Richard MacManus", - "link": "http://scripting.com/2017/06/21.html#a060651", - "description": "

Richard is one of the old school bloggers. He started ReadWriteWeb in 2003. It started as a Radio UserLand project and grew into a leading tech publication, something which I'm personally proud of.

\n

He has a new blog up and running. I've added it to my personal river here on Scripting News. He asks about where the blogrolls have gone, a topic I wrote about a couple of days ago. Richard would certainly be in my blogroll.

\n

Maybe the subscription list for my blogger's river would make a good start for my blogroll, or vice versa? Something we didn't do in the first iteration is make our rivers public. Nowadays I'm doing that routinely. A few examples are in the left sidebar here on Scripting News.

\n

Richard has turned to IndieWeb for the latest on open web tech. That's fine, but you have to look elsewhere too, because as he's discovered, they only embrace part of the open web. It's too bad they chose such an inclusive name, but have an exclusive approach. For example, they have avoided RSS, for reasons I'm sure I don't understand (I've listened, so no need to repeat the reasoning). We need all the advantages we can get because there are serious headwinds these days for blogging. RSS is serious open web technology. To not build on it is unthinkable, for me at least.

\n

Re integration between writing and reading, another topic of interest to Richard, all my rivers hook into Radio3, which is my latest linkblogging tool. For reading, I encouraged Richard to look at Electric River, it's the closest to what Radio UserLand did with aggregation in 2002. It runs on your Mac desktop, as the original did. When he wants to go all-in with rivers, nothing can take the place of River5, which is getting both modular and deep. I'm doing more work on that. Rivers have not finished evolving as far as I'm concerned.

\n", - "pubDate": "Wed, 21 Jun 2017 10:29:51 GMT", - "guid": "http://scripting.com/2017/06/21.html#a060651", - "source:outline": { - "text": "An old friend: Richard MacManus", - "created": "Wed, 21 Jun 2017 10:29:51 GMT", - "type": "outline", - "subs": [ - { - "text": "Richard is one of the old school bloggers. He started ReadWriteWeb in 2003. It started as a Radio UserLand project and grew into a leading tech publication, something which I'm personally proud of. ", - "created": "Wed, 21 Jun 2017 10:33:59 GMT", - "image": "http://scripting.com/2016/03/10/clown.png", - "permalink": "http://scripting.com/2017/06/21.html#a060659" - }, - { - "text": "He has a new blog up and running. I've added it to my personal river here on Scripting News. He asks about where the blogrolls have gone, a topic I wrote about a couple of days ago. Richard would certainly be in my blogroll.", - "created": "Wed, 21 Jun 2017 10:25:16 GMT", - "permalink": "http://scripting.com/2017/06/21.html#a060616" - }, - { - "text": "Maybe the subscription list for my blogger's river would make a good start for my blogroll, or vice versa? Something we didn't do in the first iteration is make our rivers public. Nowadays I'm doing that routinely. A few examples are in the left sidebar here on \"Scripting News\". ", - "created": "Wed, 21 Jun 2017 10:40:31 GMT", - "permalink": "http://scripting.com/2017/06/21.html#a060631" - }, - { - "text": "Richard has turned to IndieWeb for the latest on open web tech. That's fine, but you have to look elsewhere too, because as he's discovered, they only embrace part of the open web. It's too bad they chose such an inclusive name, but have an exclusive approach. For example, they have avoided \"RSS\", for reasons I'm sure I don't understand (I've listened, so no need to repeat the reasoning). We need all the advantages we can get because there are serious headwinds these days for blogging. RSS is serious open web technology. To not build on it is unthinkable, for me at least. ", - "created": "Wed, 21 Jun 2017 10:30:33 GMT", - "permalink": "http://scripting.com/2017/06/21.html#a060633" - }, - { - "text": "Re integration between writing and reading, another topic of interest to Richard, all my rivers hook into \"Radio3\", which is my latest linkblogging tool. For reading, I encouraged Richard to look at \"Electric River\", it's the closest to what Radio UserLand did with aggregation in 2002. It runs on your Mac desktop, as the original did. When he wants to go all-in with rivers, nothing can take the place of \"River5\", which is getting both modular and deep. I'm doing more work on that. Rivers have not finished evolving as far as I'm concerned. ", - "created": "Wed, 21 Jun 2017 10:32:27 GMT", - "permalink": "http://scripting.com/2017/06/21.html#a060627" - } - ], - "permalink": "http://scripting.com/2017/06/21.html#a060651" - } - }, - { - "link": "http://scripting.com/2017/06/20.html#a100647", - "description": "Anyone want to blog-debate about XML vs JSON? I've spent years using both, I think I have an objective view of the strengths of each. Imho, they are almost the same thing. XML has attributes and values, and that does make it more complex. Slightly. But you don't have to use the extra features. Look at OPML for an idea of a simple very JSON-like application of XML. Beyond that, there's really no difference. If you disagree, write a post, link to this and send me the link. I will read what you wrote, and respond, on my blog, if I have something to say. There's been so much bullshit flying around. I'd like to cut through that. ", - "pubDate": "Wed, 21 Jun 2017 02:56:47 GMT", - "guid": "http://scripting.com/2017/06/20.html#a100647", - "source:outline": { - "text": "Anyone want to blog-debate about XML vs JSON? I've spent years using both, I think I have an objective view of the strengths of each. Imho, they are almost the same thing. XML has attributes and values, and that does make it more complex. Slightly. But you don't have to use the extra features. Look at \"OPML\" for an idea of a simple very JSON-like application of XML. Beyond that, there's really no difference. If you disagree, write a post, link to this and send me the link. I will read what you wrote, and respond, on my blog, if I have something to say. There's been so much bullshit flying around. I'd like to cut through that. ", - "created": "Wed, 21 Jun 2017 02:56:47 GMT", - "type": "outline", - "image": "http://scripting.com/images/2017/06/20/penny.png", - "permalink": "http://scripting.com/2017/06/20.html#a100647" - } - } - ] - } - } -} \ No newline at end of file diff --git a/Frameworks/RSParser/RSParserTests/Resources/Subs.opml b/Frameworks/RSParser/RSParserTests/Resources/Subs.opml deleted file mode 100755 index b36fe4035..000000000 --- a/Frameworks/RSParser/RSParserTests/Resources/Subs.opml +++ /dev/null @@ -1,223 +0,0 @@ - - - - Subs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Frameworks/RSParser/RSParserTests/Resources/allthis-partial.json b/Frameworks/RSParser/RSParserTests/Resources/allthis-partial.json deleted file mode 100644 index 21b40e226..000000000 --- a/Frameworks/RSParser/RSParserTests/Resources/allthis-partial.json +++ /dev/null @@ -1 +0,0 @@ -{"description": "I just said what I said and it was wrong. Or was taken wrong.", "feed_url": "http://leancrew.com/all-this/feed.json", "title": "And now it’s all this", "items": [{"title": "Last thoughts on modifier keys", "url": "http://leancrew.com/all-this/2017/11/last-thoughts-on-modifier-keys/", "author": {"name": "Dr. Drang"}, "summary": "The first shall be last.", "date_published": "2017-11-23T21:08:29+00:00", "id": "http://leancrew.com/all-this/2017/11/last-thoughts-on-modifier-keys/", "content_html": "

When I wrote the post about ordering Mac modifier keys a few days ago, I was thinking primarily about the proper order of the symbols when writing about a keyboard shortcut, like ⌃⌥⌘P.1. I mentioned parenthetically that this order isn’t always observed when people speak about keyboard shortcuts or when they write the names of the keys out fully, as in “Command-Shift-3 takes a screenshot.”

\n

Jason Snell, in both a post at Six Colors and in conversation with John Siracusa on the lastest episode of Upgrade, took a stand against Apple’s ordering:2

\n
\n

Command is the commander! Command is the monarch of all keys! Command always comes first, in my book.

\n
\n

Siracusa agreed, and so do I. The ⌘ key is, and has always been, the key that signals a keyboard shortcut. While other modifier keys are sometimes used without ⌘—in cursor control and text selection, for example—I can’t think of any Apple applications that don’t use ⌘ to signal a keyboard shortcut for a menu item. And that primacy in shortcuts to menu items is, I think, why Apple puts it last rather than first.

\n

Keyboard shortcuts are always presented right-justified along the right edge of the menu. The most common shortcuts are just ⌘ and a letter, like ⌘N to start a new document, for example. It’s typically the variations on the basic command that get additional modifier keys, like ⌥⌘N to start a new project. If that were presented in a menu as ⌘⌥N, the menu would look wrong because the ⌘ symbols wouldn’t line up.

\n

Here’s the File menu in Safari:

\n

\"Safari

\n

There are two different New commands and three different Close commands. This, in Apple’s opinion (and mine), wouldn’t be right:

\n

\"Altered

\n

It’s not just having the ⌘ symbols aligned. The additional modifier symbols go in front because ⌘ is king and must sit next to the N or the W. The importance of the modifier decreases as you move away from the letter.

\n

It should go without saying—but I’ll say it anyway—that the letter (or number or whatever) key is the most important because nothing happens until it’s pressed.

\n

Having said all this, and despite agreeing with Apple’s symbol ordering, my ear for shortcut ordering works just like Jason’s and John’s. The main reason I use keyboard shortcut symbols in my posts instead of words is that I can read ⌥⇧⌘W and not be bothered because I don’t “hear” it as I read the symbols. “Option-Shift-Command-W,” on the other hand, gets sounded out in my head, and it sounds wrong.

\n

I suspect that’s why Apple’s own documentation sometimes gets the order wrong when the modifiers get written out as words. In speaking out the keys, “Command” is natural to put first because it announces that what’s coming is a keyboard shortcut.

\n
\n
\n
    \n
  1. \n

    Which happens to be the shortcut I use for previewing a blog post locally before publishing it. ↩︎

    \n
  2. \n
  3. \n

    In the original post, I said I didn’t know where the order was documented. A few people pointed me to both the Human Interface Guidelines and the Style Guide, where Apple gives the proper order explicitly. ↩︎

    \n
  4. \n
\n

\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "My next Mac", "url": "http://leancrew.com/all-this/2017/11/my-next-mac/", "author": {"name": "Dr. Drang"}, "summary": "Apple isn't making it easy to choose a Mac and hasn't for a few years.", "date_published": "2017-11-22T22:04:57+00:00", "id": "http://leancrew.com/all-this/2017/11/my-next-mac/", "content_html": "

Will probably be an iMac. I guess that spoils the suspense, doesn’t it?

\n

My iMac at work is the 27″ Late 2012 model, the one that came out one step before Retina came to the iMac. I don’t regret buying it, as my previous iMac (a 2006 model, I think) was absolutely on its last legs—constantly swapping to hard disk and running hot. I hadn’t meant to wait so long to replace it, but there was a long delay before that 2012 model came out, and I didn’t want to buy something that would be last year’s model almost as soon as I set it up.

\n

My home Mac is the venerable 2010 13″ MacBook Air, the first good Air. In the normal course of things, this would be the Mac I replace next, and I’ve been expecting to do so for a few years now. but…

\n

But Apple never came out with a Retina MacBook Air, choosing instead to go with the MacBook, which I find a little too far on the portable side of the portability/power spectrum. A couple of years ago, I had a crisis when my Air crapped out on me. It seemed wrong to put money into a five-year-old machine, but I wasn’t enthused about any of the MacBooks in the lineup at the time. I didn’t know the just-released 2015 MacBook Pro would turn out to be the best laptop ever made, I didn’t want to spend MacBook Pro money on my home/travel machine.

\n

The $280 logic board upgrade turned out to be a good investment, as I’m now 2½ years into my rejuvenated Air. Yes, it takes a while to wake up when I open the lid. Yes, its 128 GB SSD is tiny. No, it can’t take advantage of many of the iOS integration features that newer Macs can. And no, I don’t think it’s a good idea to install High Sierra on it. But it’s given me 30 months of faithful use, much more than I expected at the time.

\n

The announcement of the Touch Bar last year made me certain I’d be getting a MacBook Pro with it. A software-configurable set of controls seemed perfect for someone who’s always ginning up little scripts. But no one seems to like it, possibly because its configurability isn’t especially open to users. Bummer.

\n

I’ve delayed the decision on my home Mac for such a long time that now my office Mac is long in the tooth, too. Still working fine for most tasks, but just a Core 2 Duo machine that often makes me wait to scroll through long PDFs of scanned engineering and architectural drawings, something I need to do at work quite often. And no Retina.

\n

So it looks like my best bet is to buy a new iMac for work and bring my current office iMac home. This will put the power where I need it the most and will give me extra ooomph here at home. Especially with disk space (3 TB vs. 128 GB) and RAM (24GB vs. 4GB).

\n

It will be weird, though, as I haven’t had a desktop computer here at home in a dozen years. Will I enjoy being tethered to one spot in the house? And what about a travel computer?

\n

Both of these questions are made less pressing by the device I’m typing this on: a 9.7″ iPad Pro. While I agree with Gabe that it is by no means a Mac substitute, it can handle a lot of what I do at home and virtually everything I need to do on the road.1

\n

As for which iMac, I think I’ll settle on a middle-of-the-road 27″ configuration with a 3TB Fusion drive. Sort of the 2017 of what I bought in 2012.

\n
\n
\n
    \n
  1. \n

    I bought the iPad Pro last year as a sort of experiment to find out how comfortable I’d be working on it. I intend to write a full post about the results of that experiment soon, but in the meantime, you really should read Gabe’s post over at Macdrifter. I’ll probably use his post as a jumping-off point. And there may be a quiz. ↩︎

    \n
  2. \n
\n

\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "Modifier key order", "url": "http://leancrew.com/all-this/2017/11/modifier-key-order/", "author": {"name": "Dr. Drang"}, "summary": "Writing about Mac keyboard shortcuts? Make sure you put them in canonical order.", "date_published": "2017-11-20T02:22:59+00:00", "id": "http://leancrew.com/all-this/2017/11/modifier-key-order/", "content_html": "

If you write about Mac keyboard shortcuts, as I did yesterday, you should know how to do it right. Just as there’s a proper order for adjectives in English, there’s a proper order for listing the modifier keys in a shortcut.

\n

I haven’t found any documentation for this, but Apple’s preferred order is clear in how they show the modifiers in menus and how they’re displayed in the Keyboard Shortcuts Setting.

\n

\"Canonical

\n

The order is similar to how you see them down at the bottom left of your keyboard.

\n

\"Modifier

\n

Control (⌃), Option (⌥), and Command (⌘) always go in that order. The oddball is the Shift(⇧) key, which sneaks in just in front of Command.

\n

Keyboard Maestro recognizes this standard order and presents its “hot key” shortcut the same way.

\n

\"Keyboard

\n

(When people speak about keyboard shortcuts, it’s not uncommon to put Command first, e.g., “Command-Shift-3 takes a screenshot.” I’ve seen it written out that way, too. Apple is usually pretty careful to use the same order when using words as when using symbols. This page, for example, uses “Shift-Command-3,” to match the ⇧⌘3 you’d see in the Keyboard Shortcut Setti diff --git a/Frameworks/RSParser/RSParserTests/Resources/allthis.atom b/Frameworks/RSParser/RSParserTests/Resources/allthis.atom deleted file mode 100644 index 63262278a..000000000 --- a/Frameworks/RSParser/RSParserTests/Resources/allthis.atom +++ /dev/null @@ -1,520 +0,0 @@ - - - - And now it’s all this - - http://leancrew.com/all-this - I just said what I said and it was wrong. Or was taken wrong. - Thu, 23 Nov 2017 21:08:29 +0000 - en-US - hourly - 1 - http://wordpress.org/?v=4.0 - - - - -Last thoughts on modifier keys -http://leancrew.com/all-this/2017/11/last-thoughts-on-modifier-keys/ -Thu, 23 Nov 2017 21:08:29 +0000 - - - -http://leancrew.com/all-this/2017/11/last-thoughts-on-modifier-keys/ - - ordering Mac modifier keys a few days ago, I was thinking primarily about the proper order of the symbols when writing about a keyboard shortcut, like ⌃⌥⌘P.1. I mentioned parenthetically that this order isn’t always observed when people speak about keyboard shortcuts or when they write the names of the keys out fully, as in “Command-Shift-3 takes a screenshot.”]]> - - - When I wrote the post about ordering Mac modifier keys a few days ago, I was thinking primarily about the proper order of the symbols when writing about a keyboard shortcut, like ⌃⌥⌘P.1. I mentioned parenthetically that this order isn’t always observed when people speak about keyboard shortcuts or when they write the names of the keys out fully, as in “Command-Shift-3 takes a screenshot.”

-

Jason Snell, in both a post at Six Colors and in conversation with John Siracusa on the lastest episode of Upgrade, took a stand against Apple’s ordering:2

-
-

Command is the commander! Command is the monarch of all keys! Command always comes first, in my book.

-
-

Siracusa agreed, and so do I. The ⌘ key is, and has always been, the key that signals a keyboard shortcut. While other modifier keys are sometimes used without ⌘—in cursor control and text selection, for example—I can’t think of any Apple applications that don’t use ⌘ to signal a keyboard shortcut for a menu item. And that primacy in shortcuts to menu items is, I think, why Apple puts it last rather than first.

-

Keyboard shortcuts are always presented right-justified along the right edge of the menu. The most common shortcuts are just ⌘ and a letter, like ⌘N to start a new document, for example. It’s typically the variations on the basic command that get additional modifier keys, like ⌥⌘N to start a new project. If that were presented in a menu as ⌘⌥N, the menu would look wrong because the ⌘ symbols wouldn’t line up.

-

Here’s the File menu in Safari:

-

Safari File menu

-

There are two different New commands and three different Close commands. This, in Apple’s opinion (and mine), wouldn’t be right:

-

Altered Safari File menu

-

It’s not just having the ⌘ symbols aligned. The additional modifier symbols go in front because ⌘ is king and must sit next to the N or the W. The importance of the modifier decreases as you move away from the letter.

-

It should go without saying—but I’ll say it anyway—that the letter (or number or whatever) key is the most important because nothing happens until it’s pressed.

-

Having said all this, and despite agreeing with Apple’s symbol ordering, my ear for shortcut ordering works just like Jason’s and John’s. The main reason I use keyboard shortcut symbols in my posts instead of words is that I can read ⌥⇧⌘W and not be bothered because I don’t “hear” it as I read the symbols. “Option-Shift-Command-W,” on the other hand, gets sounded out in my head, and it sounds wrong.

-

I suspect that’s why Apple’s own documentation sometimes gets the order wrong when the modifiers get written out as words. In speaking out the keys, “Command” is natural to put first because it announces that what’s coming is a keyboard shortcut.

-
-
-
    -
  1. -

    Which happens to be the shortcut I use for previewing a blog post locally before publishing it. ↩︎

    -
  2. -
  3. -

    In the original post, I said I didn’t know where the order was documented. A few people pointed me to both the Human Interface Guidelines and the Style Guide, where Apple gives the proper order explicitly. ↩︎

    -
  4. -
-
-
-

[If the formatting looks odd in your feed reader, visit the original article]

]]> - -
- - -My next Mac -http://leancrew.com/all-this/2017/11/my-next-mac/ -Wed, 22 Nov 2017 22:04:57 +0000 - - - -http://leancrew.com/all-this/2017/11/my-next-mac/ - - - - - Will probably be an iMac. I guess that spoils the suspense, doesn’t it?

-

My iMac at work is the 27″ Late 2012 model, the one that came out one step before Retina came to the iMac. I don’t regret buying it, as my previous iMac (a 2006 model, I think) was absolutely on its last legs—constantly swapping to hard disk and running hot. I hadn’t meant to wait so long to replace it, but there was a long delay before that 2012 model came out, and I didn’t want to buy something that would be last year’s model almost as soon as I set it up.

-

My home Mac is the venerable 2010 13″ MacBook Air, the first good Air. In the normal course of things, this would be the Mac I replace next, and I’ve been expecting to do so for a few years now. but…

-

But Apple never came out with a Retina MacBook Air, choosing instead to go with the MacBook, which I find a little too far on the portable side of the portability/power spectrum. A couple of years ago, I had a crisis when my Air crapped out on me. It seemed wrong to put money into a five-year-old machine, but I wasn’t enthused about any of the MacBooks in the lineup at the time. I didn’t know the just-released 2015 MacBook Pro would turn out to be the best laptop ever made, I didn’t want to spend MacBook Pro money on my home/travel machine.

-

The $280 logic board upgrade turned out to be a good investment, as I’m now 2½ years into my rejuvenated Air. Yes, it takes a while to wake up when I open the lid. Yes, its 128 GB SSD is tiny. No, it can’t take advantage of many of the iOS integration features that newer Macs can. And no, I don’t think it’s a good idea to install High Sierra on it. But it’s given me 30 months of faithful use, much more than I expected at the time.

-

The announcement of the Touch Bar last year made me certain I’d be getting a MacBook Pro with it. A software-configurable set of controls seemed perfect for someone who’s always ginning up little scripts. But no one seems to like it, possibly because its configurability isn’t especially open to users. Bummer.

-

I’ve delayed the decision on my home Mac for such a long time that now my office Mac is long in the tooth, too. Still working fine for most tasks, but just a Core 2 Duo machine that often makes me wait to scroll through long PDFs of scanned engineering and architectural drawings, something I need to do at work quite often. And no Retina.

-

So it looks like my best bet is to buy a new iMac for work and bring my current office iMac home. This will put the power where I need it the most and will give me extra ooomph here at home. Especially with disk space (3 TB vs. 128 GB) and RAM (24GB vs. 4GB).

-

It will be weird, though, as I haven’t had a desktop computer here at home in a dozen years. Will I enjoy being tethered to one spot in the house? And what about a travel computer?

-

Both of these questions are made less pressing by the device I’m typing this on: a 9.7″ iPad Pro. While I agree with Gabe that it is by no means a Mac substitute, it can handle a lot of what I do at home and virtually everything I need to do on the road.1

-

As for which iMac, I think I’ll settle on a middle-of-the-road 27″ configuration with a 3TB Fusion drive. Sort of the 2017 of what I bought in 2012.

-
-
-
    -
  1. -

    I bought the iPad Pro last year as a sort of experiment to find out how comfortable I’d be working on it. I intend to write a full post about the results of that experiment soon, but in the meantime, you really should read Gabe’s post over at Macdrifter. I’ll probably use his post as a jumping-off point. And there may be a quiz. ↩︎

    -
  2. -
-
-
-

[If the formatting looks odd in your feed reader, visit the original article]

]]> -
-
- - -Modifier key order -http://leancrew.com/all-this/2017/11/modifier-key-order/ -Mon, 20 Nov 2017 02:22:59 +0000 - - - -http://leancrew.com/all-this/2017/11/modifier-key-order/ - - proper order for adjectives in English, there’s a proper order for listing the modifier keys in a shortcut.]]> - - - If you write about Mac keyboard shortcuts, as I did yesterday, you should know how to do it right. Just as there’s a proper order for adjectives in English, there’s a proper order for listing the modifier keys in a shortcut.

-

I haven’t found any documentation for this, but Apple’s preferred order is clear in how they show the modifiers in menus and how they’re displayed in the Keyboard Shortcuts Setting.

-

Canonical Mac modifier key order

-

The order is similar to how you see them down at the bottom left of your keyboard.

-

Modifier keys

-

Control (⌃), Option (⌥), and Command (⌘) always go in that order. The oddball is the Shift(⇧) key, which sneaks in just in front of Command.

-

Keyboard Maestro recognizes this standard order and presents its “hot key” shortcut the same way.

-

Keyboard Maestro hot key field

-

(When people speak about keyboard shortcuts, it’s not uncommon to put Command first, e.g., “Command-Shift-3 takes a screenshot.” I’ve seen it written out that way, too. Apple is usually pretty careful to use the same order when using words as when using symbols. This page, for example, uses “Shift-Command-3,” to match the ⇧⌘3 you’d see in the Keyboard Shortcut Settings. But even Apple slips up. On the grand Mac keyboard shortcut page, there are a few instances of “Command-Shift” instead of “Shift-Command.”)

-

The last bit of standard syntax is that the letter key in the shortcut (if there is a letter) is always presented as a capital, even when the Shift key isn’t used. I suspect this was a serious topic of discussion at Bandley 3 back in the early 80s as the Mac was being developed. They got it right. When entering a keyboard shortcut, you’re not typing a letter, you’re pressing a set of physical keys on the keyboard in front of you. The symbols on the letter keys are capitals, so that’s the appropriate way to identify those keys.

-
-

[If the formatting looks odd in your feed reader, visit the original article]

]]> -
-
- - -Command-E -http://leancrew.com/all-this/2017/11/command-e/ -Sun, 19 Nov 2017 02:02:50 +0000 - - - -http://leancrew.com/all-this/2017/11/command-e/ - - - - - Earlier this evening, Merlin tweeted out some advice we should all heed:

- - -

Even though I’ve been using a Mac for 32 years (albeit with an 8-year Linux hiatus) there were a few keyboard shortcuts in there I didn’t know about. And, just as important, several that I once knew but had fallen out of the habit of using.

-

But one of my favorites—and a big convenience when writing code or prose—isn’t on the Mac keyboard shortcut page. I wonder if the folks at Apple have forgotten it.

-

It has to do with finding text in a document. You know about ⌘F to specify a Find. That usually brings up a window or other control into which you can type (or paste) the text you’re going to be searching for. Often, there are options for controlling how the search will be performed and for replacement text. Here’s the Find window from BBEdit,

-

BBEdit Find window

-

here’s the Find window from Pages,

-

Pages Find window

-

and here’s TextEdit, which pops up a little Find section in the main window below the ruler,

-

TextEdit with Find section

-

You probably also know about ⌘G, which is usually given the menu name Find Next. It repeats the last Find command, allowing you to step through all the occurrences of that text in the document. There are usually other ways to cycle through all the found instances—Pages and TextEdit have arrow buttons you can click on—but ⌘G is the traditional way.1

-

A common situation, especially when looking through long documents or source code files, is to see a particular string of text and want to find other instances of it. You may, for example, see the definition of a function and want to search out where that function gets called later in the code. One way to do that would be to select the function name, copy it, ⌘F to bring up the text entry field, paste, and then ⌘G your way through the rest of the document.2 But the copy/⌘F/paste dance is a little clumsy.

-

Which is where ⌘E, which is typically given a menu name like Use Selection for Find, comes in.

-

BBEdit Search menu

-

Select the text you want to search for and hit ⌘E. That turns the selection into the search text, and you can go straight to ⌘G to walk through all the other occurrences.

-

I use ⌘E all the time when searching through code for function and variable names, as in the example above. It also works in Safari, Chrome, Preview, and PDF Expert, so I do similar quick searching on words I come across as I read news articles, blogs, reports, papers, etc.

-

⌘E works in other well-behaved applications, too, so you should give it a try. Note that “well-behaved applications” excludes MS Word. In Word, ⌘E centers the paragraph of text containing the selection. Of course.

-
-

Update Nov 19, 2017 8:40 AM
-A couple of things I forgot to include:

-

First, ⌘E in the Finder has, somewhat ironically, no relationship to finding. It ejects or unmounts the selected disk, assuming there is a selected disk. This shortcut is listed on Apple’s Mac shortcut page (which is why I was going to mention it), and a couple of people reminded me of it after the post was published. I guess I forgot about it because it’s a command I never use; I eject by right-clicking and selecting Eject from the menu that pops up.

-

Second, editors and word processors on iOS have not inherited this useful shortcut from the Mac. There is, of course, no ⌘ key on the software keyboard, but many of the traditional Mac shortcuts that use ⌘, ⌥, and ⇧ for moving the cursor and selecting text do work on iOS when you use an external keyboard. ⌘F and ⌘G (and its backward cousin, ⇧⌘G) often work in iOS, too, but ⌘E didn’t make the cut. Too bad.

-

Keyboard shortcuts in iOS Notes

-
-
-
-
    -
  1. -

    I believe ⌘G was originally used as the shortcut for Go To Page… in MacWrite, but it’s been Find Next for ages. 

    -
  2. -
  3. -

    Yes, source code editors in general, and BBEdit in particular, have other ways to find all the instances of a string of text, and those ways may be more appropriate in some situations. But stepping your way through each instance in turn is often the best way to see the string in full context. 

    -
  4. -
-
-
-

[If the formatting looks odd in your feed reader, visit the original article]

]]> -
-
- - -Converting fractions to decimal values -http://leancrew.com/all-this/2017/11/converting-fractions-to-decimal-values/ -Mon, 13 Nov 2017 04:35:39 +0000 - - - -http://leancrew.com/all-this/2017/11/converting-fractions-to-decimal-values/ - - - - - Recently, I’ve been getting Excel spreadsheets from clients that contain measurements in inches and/or fractions of an inch. In some cases, the cells with these measurements actually contain numbers but are displayed as fractions using one of the many formatting options Excel has.

-

Excel cell formatting options

-

In these cases, it’s easy for me to change the formatting to a standard decimal representation before exporting the sheet as a CSV file.

- -

Often, though, the measurements are entered as text. This is done by prefixing the data with a single quote mark, e.g.,

-
'2 5/16
-
-

or maybe

-
'2-5/16
-
-

These need to be converted into a decimal representation before exporting to CSV, so Pandas will recognize them as numbers rather than strings.1

-

To handle this situation, I created a Keyboard Maestro called Floatize. To use Floatize, I

-
    -
  • Select the column of data I want to convert.
  • -
  • Copy it to the clipboard.
  • -
  • Run Floatize, which converts the clipboard from a fractional representation to decimal.
  • -
  • Paste the result back into the column.
  • -
-

Here’s the macro:

-

Floatize Keyboard Maestro macro

-

It takes the clipboard, sends it to a script as standard input, and then puts the output back into the clipboard. Here’s the Python script it runs:

-
python:
- 1:  #!/usr/bin/env python
- 2:  
- 3:  from fractions import Fraction
- 4:  import re
- 5:  import sys
- 6:  
- 7:  def floatize(s):  
- 8:    if s in ['', 'n/a', 'none']:
- 9:      return ''
-10:    else:
-11:      try:
-12:        num = float(s)
-13:      except ValueError:
-14:        t = re.sub(r'(.*\d) *- *(\d.*)', r'\1 \2', s)
-15:        if t[0] == '-':
-16:          num = -float(sum(Fraction(x) for x in t[1:].split(None, 1)))
-17:        else:
-18:          num = float(sum(Fraction(x) for x in t.split(None, 1)))
-19:      return "{:f}".format(num)
-20:    
-21:  for line in sys.stdin.read().splitlines():
-22:    item = line.strip().lower()
-23:    print floatize(item)
-
-

The bulk of the fraction parsing is done by the fractions module in either Line 16 or Line 18. The rest of the script is an attempt to manipulate the oddball types of data I get into a format that fractions understands. Here’s what it can handle:

-
    -
  • Blank, n/a or none entries get turned into an empty string (Lines 8–9). Note that entries are converted to lower case in Line 22, before being passed to the floatize function, so this handles N/A and None, too.
  • -
  • Entries already in decimal format get converted to a float (Line 12) and then output in decimal format (Line 19). This is basically a pass-through.
  • -
  • Entries with a dash between the whole and fractional parts get the dash removed (Line 14).
  • -
  • Negative entries (Line 15) have their whole and fractional parts added together with the sign reversed (Line 16), and then output in decimal format (Line 19).
  • -
  • Positive entries have their whole and fractional parts added together (Line 18), and then output in decimal format (Line 19).
  • -
  • Entries that don’t look like any of the above cause an error.
  • -
-

The macro works perfectly in Numbers and BBEdit, but not in Excel. For some reason, nothing happens to the clipboard if I run the Floatize macro while Excel is the active application. I can, however, copy the column of fractions, activate another app, run Floatize, switch back to Excel and paste the converted clipboard. Another example of Excel not acting like a proper Mac app and another reason I move data out of Excel as quickly as I can.

-

Despite its inability to work inside Excel, this macro has been very handy over the past couple of weeks. I’ve used it to clean up several data sets that were loaded with fractions.

-

By the way, I will block anyone who tweets that I wouldn’t have this problem if I used the metric system.

-
-

Update Nov 13, 2017 10:04 PM  -The Twitter consensus is my problem in using this macro in Excel stems from Excel’s clipboard dumbassery. It apparently uses a private clipboard instead of the system clipboard until you bring another application to the front. Hence the macro’s inability to convert the clipboard unless I switched away from Excel.

-

Jimmy Hartington suggested this workaround:

-
-
-@drdrang Excel does something weird with the clipboard as you noticed.
Try this. Copy in Excel. Press Escape. Run the macro.
-By pressing Escape I think Excel stops messing with the clipboard. -
Jimmy Hartington (@jimmyhartington) Nov 12 2017 11:06 PM
-
-
-

This works perfectly and pointed me in the direction to make a better version of the macro, one that does all the copying, converting, and pasting:

-

New Floatize Keyboard Maestro macro

-

Note the conditional action that simulates the pressing of the Escape key only if Excel is the front application. This is necessary because Escape causes Numbers to deselect the column, which screws up the paste action at the end of the macro.

-

With this macro I can now select the column that needs converting and just press ⌃⌥⌘F to do the conversion in a single step. Thanks, Jimmy!

-
-
-
-
    -
  1. -

    There are probably ways to get Pandas to do the conversion as it imports the data, but I feel more comfortable—more in control—if I do it ahead of time. ↩︎

    -
  2. -
-
-
-

[If the formatting looks odd in your feed reader, visit the original article]

]]> -
-
- - -A modest proposal -http://leancrew.com/all-this/2017/11/a-modest-proposal/ -Tue, 07 Nov 2017 03:27:11 +0000 - - - -http://leancrew.com/all-this/2017/11/a-modest-proposal/ - - on my mind, I’d like to make a small complaint and suggestion to the folks at MacStories: some of your quarterly graphs could use a little scrubbing; they’d be much easier on the eyes if they were cleaned up a bit.]]> - - - With Apple sales and graphing on my mind, I’d like to make a small complaint and suggestion to the folks at MacStories: some of your quarterly graphs could use a little scrubbing; they’d be much easier on the eyes if they were cleaned up a bit.

-

I’m talking about the ones that look like this:

-

MacStories unit sales graph

-

My complaint is with the clutter along the x-axis. That uniform gray mass of text detracts from the data above it. And although putting a label at each quarter seems to provide more information to the reader, the reader actually gets less out of it because it’s too hard to read. The axis would be improved if it had just yearly Q1 labels. Maybe even one label every other year. Fewer labels would mean they could be turned back horizontal, which would be easier to read and would give more vertical space for the data.

-

I suspect every data point is labeled because that’s the way Numbers wants to do it, and MacStories’ automated system for generating these graphs follows the Numbers defaults. But if you were making these graphs by hand, you’d never label the x-axis this way. An automated system shouldn’t, either.

-

On the Mac, Numbers has a way to skip labels on the x-axis by selecting Custom Category Intervals from the Category Labels popup menu and then choosing how often to put the labels.

-

Mac Numbers axis options

-

Sadly, I can’t find that option in the iOS version. Maybe it’s tucked away in a less obvious place.

-

iOS Numbers axis options

-

Personally, I’m not a fan of either version of Numbers when it comes to making graphs because I like way more control than they give. But Numbers (like most graphing programs and libraries) can make nice graphs if you’re willing to break away from its defaults.

-

I don’t really want to pick on MacStories. Their graphs are as good as, if not better than, what you typically find in blogs and news sites. Most graphs, unfortunately, are based on the default settings of the software that made them, and come out looking a little clumsy.

-

I read MacStories because it has stylish writing. I’d like its graphs to be just as stylish.

-
-

Update Nov 7, 2017 8:55 PM
-I was informed on Twitter by two authoritative sources (this one and this one) that the MacStories graphs are made with Excel, not Numbers. I haven’t used Excel for graphing since the mid-90s, but even then it had more customization options than Numbers has now. So I’m crossing my fingers for cleaner x-axes in January’s graphs.

-
-
-

[If the formatting looks odd in your feed reader, visit the original article]

]]> -
-
- - -Apple sales graphs and the iPhone 7 -http://leancrew.com/all-this/2017/11/apple-sales-graphs-and-the-iphone-7/ -Mon, 06 Nov 2017 02:53:19 +0000 - - - -http://leancrew.com/all-this/2017/11/apple-sales-graphs-and-the-iphone-7/ - - sales and revenue figures, which means a boatload of graphs from all your fave rave Apple-oriented bloggers. I’m a few days late, as usual, despite having long ago written scripts to generate these graphs.]]> - - - Last week Apple released its 2017 Q4 (which everyone else’s calendar says is 2017 Q3) sales and revenue figures, which means a boatload of graphs from all your fave rave Apple-oriented bloggers. I’m a few days late, as usual, despite having long ago written scripts to generate these graphs.

-

I don’t make as many graphs as everyone else, partly because I’m lazy, but mainly because don’t care about revenue. My concern is the popularity of the devices I use, not how expensive they are. I want third-party developers to keep writing apps for them, and that means there has to be a market for them out there.

-

Here’s the summary graph, with the sales figures for the three main Apple products. The dots are the quarterly sales and the lines are the four-quarter moving averages.

-

Apple sales

-

The iPhone dominates the scale of this graphs, so it’s helpful to also see the iPad and Mac on their own graphs.

-

iPad sales

-

Mac sales

-

The good news is the second straight quarter of year-over-year increase in iPad sales after its long, well-documented slide since 2013. Additional good news is the Mac’s slow but consistent rise over the past year—four straight quarters of year-over-year sales increases. Imagine how much better the figures would be if people really liked the Touch Bar.

-

I think its fair to say, though, that the iPhone 7 was something of a dud. It had a good start, but its non-intro quarters had almost exactly the same sales as the 6S had. Look at the final three quarters of each.

-

Apple sales annotated

-

As I’ve argued before, the iPhone 6S sales looked lackluster only in comparison to the gangbusters popularity of the iPhone 6. If one ignored the year of the 6, the 6S’s sales were more or less on the same upward trend as the previous three editions. The iPhone 7 was on that same trend for its first quarter but went dead flat after that.

-

Apple’s report in January will be very interesting.

-
-

[If the formatting looks odd in your feed reader, visit the original article]

]]> -
-
- - -Another one-off Keyboard Maestro macro -http://leancrew.com/all-this/2017/11/another-one-off-keyboard-maestro-macro/ -Fri, 03 Nov 2017 02:02:41 +0000 - - - -http://leancrew.com/all-this/2017/11/another-one-off-keyboard-maestro-macro/ - - - - - Do you use Keyboard Maestro (or AppleScript or whatever) for one-time, throwaway macros as often as you should? I know I don’t, but I did put one together a couple of days ago and used a feature I’d never tried before.

-

I had one of those recalcitrant PDFs that I often get from clients. This one was 25–30 pages long, each an E-sized floor plan drawing for a building. The drawings were all black-and-white, but the PDF had color annotations added. I needed to add my own annotations to most of the pages, but something about the format of the file made it very cumbersome to work with. I tried Preview, PDF Expert, and PDfpen Pro, and they all were glacially slow when panning, zooming, and switching pages.

-

So I broke the file up into individual pages using PDFtk:

-
pdftk drawings.pdf burst
-
-

The single-page files didn’t make me wait for the spinning beach ball, so I was able to add my annotations quickly in PDF Expert. Then came an impasse.

-

I wanted to email the drawings with my annotations back to the client and to some other parties, but they were too big to fit in an email. I could use multiple emails, but that’s a recipe for losing some of the files. I could use a Dropbox link, but I had a sense that one of the other parties wouldn’t understand how that worked. What seemed best was to convert the files to JPEGs at the lowest legible resolution, zip them together, and send the zipped file in a single email.1

-

My normal practice would be to use sips for this, because I can issue a single command to convert any number of files. But I soon learned that sips doesn’t handle annotations properly when converting the format of a file from PDF to JPEG. In my brief testing, I found that neither my annotations or the ones that came from the client were visible in the converted JPEGs.

-

Preview, though, can export a PDF as a JPEG with the annotations intact and visible. Which presumably means that sips and Preview are using different code bases for the conversion. Whatever.

-

The problem with using Preview is I’d have to convert every file by hand. Not the most burdensome job I’ve ever had, but one that’s boring and susceptible to error. Enter Keyboard Maestro.

-

Here’s the macro that exports the current file in Preview to a JPEG and closes it:

-

Convert to JPEG macro

-

You’ll note there’s no step for setting the resolution for the exported file. That’s because once it’s set, it doesn’t change from one export to the next.

-

I didn’t try to have the macro open each PDF in turn, because I didn’t trust myself to do that right. I just opened all of them and then ran the macro by pressing ⌃⌥⌘J repeatedly. The macro closed each file after exporting it, leaving the next window ready to be operated on.

-

The new (for me) thing with this macro was the “and drag to” part at the bottom of the third step. That’s what moved the popup menu selection from PDF up to JPEG.

-

Preview export sheet

-

I figured out how much to drag by taking a screenshot like the one above and using a selection in Acorn to measure the vertical distance from the center of PDF to the center of JPEG. No trial and error.

-

With 25–30 files to convert, it’s possible I did save time with this macro. But the main reason I made it was to avoid the tedium and the likelihood of error on my part. These are not independent—I’m far more prone to make errors when the task is repetitive and doesn’t maintain my attention.

-
-
-
    -
  1. -

    No, a Dropbox link is really no more complicated than a zip file, but zip files are more familiar to more people. And although zipping JPEGs doesn’t make them smaller, it does package them up in a way that naive Windows users are usually comfortable with. It would be lovely if I always worked with people whose computer skills were trustworthy, but that’s not the world I live in. ↩︎

    -
  2. -
-
-
-

[If the formatting looks odd in your feed reader, visit the original article]

]]> -
-
- - -Binomial baseball -http://leancrew.com/all-this/2017/10/binomial-baseball/ -Mon, 30 Oct 2017 18:14:47 +0000 - - - -http://leancrew.com/all-this/2017/10/binomial-baseball/ - - - - - While reading a recap of last night’s World Series games, I saw this statistic: of the 65 Series that have had a sixth game, the team with the 3–2 lead has won the Series 43 times. This was, I think, intended to show us that the Astros have a strong chance to beat out the Dodgers for the title. And they do. But not as strong as you might expect.

-

If the teams were evenly balanced and each game independent of the others, we would expect the team with the 3–2 lead to win 75% of the time. 50% of the time they’d win the sixth game and the Series would be over; 25% of the time (50% of the other 50%) they’d win in the seventh game. So the leading team “should” have won the Series 48 or 49 times out of 65, not 43 times.

-

Is this 5 or 6 game difference meaningful? For that we need to do some calculations using the binomial distribution. Python’s SciPy set of libraries has a subsection of statistical modules, including one for binomial distribution calculations. We can import it this way:

-
python:
-from scipy.stats import binom
-
-

Let’s start by figuring out the probability that the leading team would win 43 times in 65 trials. With a 75% probability of winning the Series in each trial, the probability of 43 Series wins in 65 chances is calculated through

-
python:
-binom.pmf(43, 65, .75)
-
-

where the pmf function gets its name from the standard abbreviation for “probability mass function.” The answer is 0.029 or just under 3%. This makes it seem very unlikely that our assumption of 50–50 games would lead to only 43 Series wins for the leading team.

-

But that isn’t the way these sorts of calculations are normally done. If we want to find out if a seemingly out-of-whack result is “statistically significant,” we should look at the probability of results that are at least as far away from our expectations as the actual result was. In our case, that means looking not only at the probability of 43 Series wins out of 65 chances, but also 42 wins, 41 wins, and so on. We then add up all of these “at least as weird” probabilities.

-

The usual terminology for this sort of summation is “cumulative distribution function,” and the binom module has a function for it:

-
python:
-binom.cdf(43, 65, .75)
-
-

The result is 0.0695, or about 7%. Another way of looking at this is that if our assumption of 50–50 games were correct, there’s a 93% chance that the leading team would win the Series more than 43 times in 65 chances.

-

In hypothesis testing, the value 0.0695 is called the p-value, -and it’s common in many fields to consider a result statistically significant if its p-value is less than 0.05. Using that criterion, we would not take the difference between our “null hypothesis” of 50–50 games and the World Series history as statistically significant.1

-

But it’s something for Dodgers fans to cling to.

-
-
-
    -
  1. -

    Yes, I’ve been a little breezy here with my definition of null and alternate hypotheses and one-sided vs. two-sided rejection areas, but it’s just baseball. ↩︎

    -
  2. -
-
-
-

[If the formatting looks odd in your feed reader, visit the original article]

]]> -
-
- - -Judas -http://leancrew.com/all-this/2017/10/judas/ -Sat, 28 Oct 2017 14:33:47 +0000 - - - -http://leancrew.com/all-this/2017/10/judas/ - - Shadows in the Night and Fallen Angels, were strong clues that I was going to be seeing a Bob Dylan infected by the Great American Songbook.]]> - - - I can’t say I wasn’t warned. The concert’s promotional artwork (especially those fonts) and the previous two albums, Shadows in the Night and Fallen Angels, were strong clues that I was going to be seeing a Bob Dylan infected by the Great American Songbook.

-

Dylan concert promo

-

So yes, we got “Melancholy Mood” and “Autumn Leaves” and “The September of My Years” with Bob crooning into a big microphone and holding its stand at a jaunty angle. But that’s not what I disliked about the concert. In fact, those songs were done pretty well, and Dylan’s voice sounded better than any previous concert I’ve been to. It was what he did to his own songs that was frustrating.

-

No one in his right mind goes to a Dylan concert expecting to hear his classics played or sung the way they were done originally. But the treatment they’re given is always interesting and usually fun. Not this time. Apart from “It Ain’t Me, Babe” and “Highway 61 Revisited,” the Dylan songs suffered from arrangements that were, I guess, meant to match the style of the non-Dylan songs. Under these conditions, you can’t expect the songs to rock, but you can expect them to swing. And the band just didn’t swing.

-

There was a sameness to Charlie Sexton’s guitar work throughout the night. Despite changing instruments several times, his sound was constrained and repetitive. But when a band doesn’t swing, it’s mainly because of the rhythm section. These are talented musicians who can play in any style, so if they didn’t find a groove it must have been because Bob didn’t want them to.

-

As a result, he lost the audience. Many left early and those who hung in there stayed glued to their seats until the encores. Can you imagine a Dylan audience not standing to “Tangled Up in Blue”?

-

The optimist in me notes that Dylan never sticks with anything for long, and after two albums of standards he may be ready for the next thing. I hope he comes back to the notion that that it’s his songs, and the blues and country/folk they came out of, that are truly the Great American Songbook and gives them the treatment they deserve.

-
-

[If the formatting looks odd in your feed reader, visit the original article]

]]> -
-
- - -Icons -http://leancrew.com/all-this/2017/10/icons/ -Fri, 27 Oct 2017 01:39:06 +0000 - - - -http://leancrew.com/all-this/2017/10/icons/ - - Brett Terpstra’s iTunesIcon Automator app, which just got an update.]]> - - - I have some code sitting around here somewhere for extracting the images (app icons, book covers, album art, etc.) from the various iTunes and App Stores. But I think it’ll be easier to use Brett Terpstra’s iTunesIcon Automator app, which just got an update.

-

Apart from its appeal to my laziness, Brett’s little app has a cool icon that I took an instant liking to.

-

iTunesIcon icon

-

If you work around industrial and construction equipment, Brett’s icon should bring a smile of familiarity. It’s highly reminiscent of the hands in safety signs and decals that are in dire straits.

-

Safety symbols

-

Images from SafetySign.com

-

I’m sure your fingers are safe when using iTunesIcon.

-
-

[If the formatting looks odd in your feed reader, visit the original article]

]]> -
-
- - -Feed reading -http://leancrew.com/all-this/2017/10/feed-reading/ -Mon, 23 Oct 2017 00:10:42 +0000 - - - -http://leancrew.com/all-this/2017/10/feed-reading/ - - a nice article today on how he uses Feedbin to handle his reading on the web. If you follow Gabe—and if not, why don’t you?—it will not surprise you to learn that he’s formidably organized. I don’t see myself following in his footsteps, but it’s always useful to learn how smart people do things. His article also reminded me that I’ve been meaning to write about my feed reading setup.]]> - - - Gabe Weatherhead has a nice article today on how he uses Feedbin to handle his reading on the web. If you follow Gabe—and if not, why don’t you?—it will not surprise you to learn that he’s formidably organized. I don’t see myself following in his footsteps, but it’s always useful to learn how smart people do things. His article also reminded me that I’ve been meaning to write about my feed reading setup.

-

A couple of years ago, I let my subscription to Feed Wrangler lapse and started using a homemade, web-based RSS reading system. The heart of the system is still the script described in this post, but with a some changes as I thought of better ways of doing things.

-

The biggest change came in the past few months. Initially, I created a single static web page with all of today’s articles from the feeds I subscribe to. A cron task updated the page a few times an hour throughout the day. In this system, “today” was defined as “from 10:00 pm last night until now,” and the page would grow in size from morning to night.

-

The advantage of this temporal arrangement from a programming point of view was that I didn’t have to write any code to keep track of whether I’d already read an article or not, and there were no external dependencies. If it was published “today,” it got on the page.

-

The disadvantage was from the reading point of view. As I visited the page throughout the day, it became more and more filled with article I’d already read. This wasn’t as terrible as you might think. The articles were arranged in reverse chronological order by publication time, so the ones I’d read were typically at the bottom of the page. I say “typically” because some feeds—XKCD comes to mind—are very bad at providing accurate publication times and their articles would sometimes end up at the bottom despite being recently published.

-

Eliminating the reading disadvantage meant keeping track of what I’d read and showing only what I hadn’t—eliminating the programming advantage. I decided to keep track of read articles in an SQLite database and to add items to that database through a button placed at the bottom of each article on my RSS page.

-

RSS buttons

-

This meant

-
    -
  1. Building a database that would uniquely identify every article. This was pretty simple. Each record has just two fields: the name of the website and the unique article ID (which is often just the article’s URL but is sometimes a long alphanumeric string generated by the site’s blogging software).
  2. -
  3. Altering the existing script that builds the RSS page to filter out feed items that are in the database. Because Python has an SQLite module as part of its standard library and the syntax of SQL commands is straightforward, this wasn’t as tricky as I thought it would be. In fact, the new code is easier to read than the time-based filtering code I removed.
  4. -
  5. Writing a server script (basically just a CGI script) to add an article to the database when given the blog name and article ID via the POST method. It’s been a while since I last wrote a CGI script, but it was like riding a bicycle.
  6. -
  7. Adding some JavaScript with XMLHttpRequest to the RSS page to call the server script when a button is pressed. This took the most time, mainly because everyone in the world (except me) knows how to do AJAX now, and finding references written at an appropriately low level was harder than I expected. I found this Stack Overflow discussion helpful.
  8. -
-

So now I usually tap the Mark as read button when I get to the end of an article. If it’s a long article that I want to read later, I don’t mark it as read, and it’ll be there the next time I bring up the RSS page.

-

Fearing I’d forget how to use XMLHttpRequest, I quickly included another form at the end of each article for adding that article to my Pinboard account. I didn’t bother adding labels to the text field, because I’m the only one who uses this and I know the field is for tags. I did, however, include some DOM stuff to make it obvious when I’d marked an article as read or added it to Pinboard.

-

RSS buttons marked

-

What I like about this system is how portable and (I hope) future-proof it is. I’ve been reluctant to sign on with Feedbin or Feedly or BazQux or any of the other Google Reader replacements because I worry they’ll write a Medium post and disappear with my subscription list and whatever organization scheme I’ve created. My system can run on any web server with Python, SQLite, and a cgi-bin directory. I think that’ll mean “any server, anywhere” for a very long time.

-
-

[If the formatting looks odd in your feed reader, visit the original article]

]]> -
-
- -
-
- diff --git a/Frameworks/RSParser/RSParserTests/Resources/allthis.json b/Frameworks/RSParser/RSParserTests/Resources/allthis.json deleted file mode 100644 index ee6f0fcc6..000000000 --- a/Frameworks/RSParser/RSParserTests/Resources/allthis.json +++ /dev/null @@ -1 +0,0 @@ -{"description": "I just said what I said and it was wrong. Or was taken wrong.", "feed_url": "http://leancrew.com/all-this/feed.json", "title": "And now it’s all this", "items": [{"title": "Last thoughts on modifier keys", "url": "http://leancrew.com/all-this/2017/11/last-thoughts-on-modifier-keys/", "author": {"name": "Dr. Drang"}, "summary": "The first shall be last.", "date_published": "2017-11-23T21:08:29+00:00", "id": "http://leancrew.com/all-this/2017/11/last-thoughts-on-modifier-keys/", "content_html": "

When I wrote the post about ordering Mac modifier keys a few days ago, I was thinking primarily about the proper order of the symbols when writing about a keyboard shortcut, like ⌃⌥⌘P.1. I mentioned parenthetically that this order isn’t always observed when people speak about keyboard shortcuts or when they write the names of the keys out fully, as in “Command-Shift-3 takes a screenshot.”

\n

Jason Snell, in both a post at Six Colors and in conversation with John Siracusa on the lastest episode of Upgrade, took a stand against Apple’s ordering:2

\n
\n

Command is the commander! Command is the monarch of all keys! Command always comes first, in my book.

\n
\n

Siracusa agreed, and so do I. The ⌘ key is, and has always been, the key that signals a keyboard shortcut. While other modifier keys are sometimes used without ⌘—in cursor control and text selection, for example—I can’t think of any Apple applications that don’t use ⌘ to signal a keyboard shortcut for a menu item. And that primacy in shortcuts to menu items is, I think, why Apple puts it last rather than first.

\n

Keyboard shortcuts are always presented right-justified along the right edge of the menu. The most common shortcuts are just ⌘ and a letter, like ⌘N to start a new document, for example. It’s typically the variations on the basic command that get additional modifier keys, like ⌥⌘N to start a new project. If that were presented in a menu as ⌘⌥N, the menu would look wrong because the ⌘ symbols wouldn’t line up.

\n

Here’s the File menu in Safari:

\n

\"Safari

\n

There are two different New commands and three different Close commands. This, in Apple’s opinion (and mine), wouldn’t be right:

\n

\"Altered

\n

It’s not just having the ⌘ symbols aligned. The additional modifier symbols go in front because ⌘ is king and must sit next to the N or the W. The importance of the modifier decreases as you move away from the letter.

\n

It should go without saying—but I’ll say it anyway—that the letter (or number or whatever) key is the most important because nothing happens until it’s pressed.

\n

Having said all this, and despite agreeing with Apple’s symbol ordering, my ear for shortcut ordering works just like Jason’s and John’s. The main reason I use keyboard shortcut symbols in my posts instead of words is that I can read ⌥⇧⌘W and not be bothered because I don’t “hear” it as I read the symbols. “Option-Shift-Command-W,” on the other hand, gets sounded out in my head, and it sounds wrong.

\n

I suspect that’s why Apple’s own documentation sometimes gets the order wrong when the modifiers get written out as words. In speaking out the keys, “Command” is natural to put first because it announces that what’s coming is a keyboard shortcut.

\n
\n
\n
    \n
  1. \n

    Which happens to be the shortcut I use for previewing a blog post locally before publishing it. ↩︎

    \n
  2. \n
  3. \n

    In the original post, I said I didn’t know where the order was documented. A few people pointed me to both the Human Interface Guidelines and the Style Guide, where Apple gives the proper order explicitly. ↩︎

    \n
  4. \n
\n

\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "My next Mac", "url": "http://leancrew.com/all-this/2017/11/my-next-mac/", "author": {"name": "Dr. Drang"}, "summary": "Apple isn't making it easy to choose a Mac and hasn't for a few years.", "date_published": "2017-11-22T22:04:57+00:00", "id": "http://leancrew.com/all-this/2017/11/my-next-mac/", "content_html": "

Will probably be an iMac. I guess that spoils the suspense, doesn’t it?

\n

My iMac at work is the 27″ Late 2012 model, the one that came out one step before Retina came to the iMac. I don’t regret buying it, as my previous iMac (a 2006 model, I think) was absolutely on its last legs—constantly swapping to hard disk and running hot. I hadn’t meant to wait so long to replace it, but there was a long delay before that 2012 model came out, and I didn’t want to buy something that would be last year’s model almost as soon as I set it up.

\n

My home Mac is the venerable 2010 13″ MacBook Air, the first good Air. In the normal course of things, this would be the Mac I replace next, and I’ve been expecting to do so for a few years now. but…

\n

But Apple never came out with a Retina MacBook Air, choosing instead to go with the MacBook, which I find a little too far on the portable side of the portability/power spectrum. A couple of years ago, I had a crisis when my Air crapped out on me. It seemed wrong to put money into a five-year-old machine, but I wasn’t enthused about any of the MacBooks in the lineup at the time. I didn’t know the just-released 2015 MacBook Pro would turn out to be the best laptop ever made, I didn’t want to spend MacBook Pro money on my home/travel machine.

\n

The $280 logic board upgrade turned out to be a good investment, as I’m now 2½ years into my rejuvenated Air. Yes, it takes a while to wake up when I open the lid. Yes, its 128 GB SSD is tiny. No, it can’t take advantage of many of the iOS integration features that newer Macs can. And no, I don’t think it’s a good idea to install High Sierra on it. But it’s given me 30 months of faithful use, much more than I expected at the time.

\n

The announcement of the Touch Bar last year made me certain I’d be getting a MacBook Pro with it. A software-configurable set of controls seemed perfect for someone who’s always ginning up little scripts. But no one seems to like it, possibly because its configurability isn’t especially open to users. Bummer.

\n

I’ve delayed the decision on my home Mac for such a long time that now my office Mac is long in the tooth, too. Still working fine for most tasks, but just a Core 2 Duo machine that often makes me wait to scroll through long PDFs of scanned engineering and architectural drawings, something I need to do at work quite often. And no Retina.

\n

So it looks like my best bet is to buy a new iMac for work and bring my current office iMac home. This will put the power where I need it the most and will give me extra ooomph here at home. Especially with disk space (3 TB vs. 128 GB) and RAM (24GB vs. 4GB).

\n

It will be weird, though, as I haven’t had a desktop computer here at home in a dozen years. Will I enjoy being tethered to one spot in the house? And what about a travel computer?

\n

Both of these questions are made less pressing by the device I’m typing this on: a 9.7″ iPad Pro. While I agree with Gabe that it is by no means a Mac substitute, it can handle a lot of what I do at home and virtually everything I need to do on the road.1

\n

As for which iMac, I think I’ll settle on a middle-of-the-road 27″ configuration with a 3TB Fusion drive. Sort of the 2017 of what I bought in 2012.

\n
\n
\n
    \n
  1. \n

    I bought the iPad Pro last year as a sort of experiment to find out how comfortable I’d be working on it. I intend to write a full post about the results of that experiment soon, but in the meantime, you really should read Gabe’s post over at Macdrifter. I’ll probably use his post as a jumping-off point. And there may be a quiz. ↩︎

    \n
  2. \n
\n

\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "Modifier key order", "url": "http://leancrew.com/all-this/2017/11/modifier-key-order/", "author": {"name": "Dr. Drang"}, "summary": "Writing about Mac keyboard shortcuts? Make sure you put them in canonical order.", "date_published": "2017-11-20T02:22:59+00:00", "id": "http://leancrew.com/all-this/2017/11/modifier-key-order/", "content_html": "

If you write about Mac keyboard shortcuts, as I did yesterday, you should know how to do it right. Just as there’s a proper order for adjectives in English, there’s a proper order for listing the modifier keys in a shortcut.

\n

I haven’t found any documentation for this, but Apple’s preferred order is clear in how they show the modifiers in menus and how they’re displayed in the Keyboard Shortcuts Setting.

\n

\"Canonical

\n

The order is similar to how you see them down at the bottom left of your keyboard.

\n

\"Modifier

\n

Control (⌃), Option (⌥), and Command (⌘) always go in that order. The oddball is the Shift(⇧) key, which sneaks in just in front of Command.

\n

Keyboard Maestro recognizes this standard order and presents its “hot key” shortcut the same way.

\n

\"Keyboard

\n

(When people speak about keyboard shortcuts, it’s not uncommon to put Command first, e.g., “Command-Shift-3 takes a screenshot.” I’ve seen it written out that way, too. Apple is usually pretty careful to use the same order when using words as when using symbols. This page, for example, uses “Shift-Command-3,” to match the ⇧⌘3 you’d see in the Keyboard Shortcut Settings. But even Apple slips up. On the grand Mac keyboard shortcut page, there are a few instances of “Command-Shift” instead of “Shift-Command.”)

\n

The last bit of standard syntax is that the letter key in the shortcut (if there is a letter) is always presented as a capital, even when the Shift key isn’t used. I suspect this was a serious topic of discussion at Bandley 3 back in the early 80s as the Mac was being developed. They got it right. When entering a keyboard shortcut, you’re not typing a letter, you’re pressing a set of physical keys on the keyboard in front of you. The symbols on the letter keys are capitals, so that’s the appropriate way to identify those keys.


\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "Command-E", "url": "http://leancrew.com/all-this/2017/11/command-e/", "author": {"name": "Dr. Drang"}, "summary": "One of my favorite and little-known (even by Apple) Mac system level keyboard shortcuts.", "date_published": "2017-11-19T02:02:50+00:00", "id": "http://leancrew.com/all-this/2017/11/command-e/", "content_html": "

Earlier this evening, Merlin tweeted out some advice we should all heed:

\n

Per today's @thetalkshow, here's an Apple Support doc that can change your life. I dare you not to find something new here.https://t.co/37FuPPUbYK

— Merlin Mann (@hotdogsladies) November 18, 2017
\n\n

Even though I’ve been using a Mac for 32 years (albeit with an 8-year Linux hiatus) there were a few keyboard shortcuts in there I didn’t know about. And, just as important, several that I once knew but had fallen out of the habit of using.

\n

But one of my favorites—and a big convenience when writing code or prose—isn’t on the Mac keyboard shortcut page. I wonder if the folks at Apple have forgotten it.

\n

It has to do with finding text in a document. You know about ⌘F to specify a Find. That usually brings up a window or other control into which you can type (or paste) the text you’re going to be searching for. Often, there are options for controlling how the search will be performed and for replacement text. Here’s the Find window from BBEdit,

\n

\"BBEdit

\n

here’s the Find window from Pages,

\n

\"Pages

\n

and here’s TextEdit, which pops up a little Find section in the main window below the ruler,

\n

\"TextEdit

\n

You probably also know about ⌘G, which is usually given the menu name Find Next. It repeats the last Find command, allowing you to step through all the occurrences of that text in the document. There are usually other ways to cycle through all the found instances—Pages and TextEdit have arrow buttons you can click on—but ⌘G is the traditional way.1

\n

A common situation, especially when looking through long documents or source code files, is to see a particular string of text and want to find other instances of it. You may, for example, see the definition of a function and want to search out where that function gets called later in the code. One way to do that would be to select the function name, copy it, ⌘F to bring up the text entry field, paste, and then ⌘G your way through the rest of the document.2 But the copy/⌘F/paste dance is a little clumsy.

\n

Which is where ⌘E, which is typically given a menu name like Use Selection for Find, comes in.

\n

\"BBEdit

\n

Select the text you want to search for and hit ⌘E. That turns the selection into the search text, and you can go straight to ⌘G to walk through all the other occurrences.

\n

I use ⌘E all the time when searching through code for function and variable names, as in the example above. It also works in Safari, Chrome, Preview, and PDF Expert, so I do similar quick searching on words I come across as I read news articles, blogs, reports, papers, etc.

\n

⌘E works in other well-behaved applications, too, so you should give it a try. Note that “well-behaved applications” excludes MS Word. In Word, ⌘E centers the paragraph of text containing the selection. Of course.

\n
\n

Update Nov 19, 2017 8:40 AM
\nA couple of things I forgot to include:

\n

First, ⌘E in the Finder has, somewhat ironically, no relationship to finding. It ejects or unmounts the selected disk, assuming there is a selected disk. This shortcut is listed on Apple’s Mac shortcut page (which is why I was going to mention it), and a couple of people reminded me of it after the post was published. I guess I forgot about it because it’s a command I never use; I eject by right-clicking and selecting Eject from the menu that pops up.

\n

Second, editors and word processors on iOS have not inherited this useful shortcut from the Mac. There is, of course, no ⌘ key on the software keyboard, but many of the traditional Mac shortcuts that use ⌘, ⌥, and ⇧ for moving the cursor and selecting text do work on iOS when you use an external keyboard. ⌘F and ⌘G (and its backward cousin, ⇧⌘G) often work in iOS, too, but ⌘E didn’t make the cut. Too bad.

\n

\"Keyboard

\n
\n
\n
\n
    \n
  1. \n

    I believe ⌘G was originally used as the shortcut for Go To Page… in MacWrite, but it’s been Find Next for ages. 

    \n
  2. \n
  3. \n

    Yes, source code editors in general, and BBEdit in particular, have other ways to find all the instances of a string of text, and those ways may be more appropriate in some situations. But stepping your way through each instance in turn is often the best way to see the string in full context. 

    \n
  4. \n
\n

\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "Converting fractions to decimal values", "url": "http://leancrew.com/all-this/2017/11/converting-fractions-to-decimal-values/", "author": {"name": "Dr. Drang"}, "summary": "A Keyboard Maestro macro for cleaning up numerical values.", "date_published": "2017-11-13T04:35:39+00:00", "id": "http://leancrew.com/all-this/2017/11/converting-fractions-to-decimal-values/", "content_html": "

Recently, I’ve been getting Excel spreadsheets from clients that contain measurements in inches and/or fractions of an inch. In some cases, the cells with these measurements actually contain numbers but are displayed as fractions using one of the many formatting options Excel has.

\n

\"Excel

\n

In these cases, it’s easy for me to change the formatting to a standard decimal representation before exporting the sheet as a CSV file.

\n
\n

Aside
\nThe only reason I own Excel is to be able to open spreadsheets that other people send me and export them into some other usable format. This wasn’t always the case. I used the hell out of Excel in the late 80s and early 90s, when it was still a proper Mac app. But somewhere there in the early to mid 90s, Microsoft turned Excel into a Windows app that ran on the Mac and ruined its user interface. I stopped using it shortly thereafter. In the quarter-century since, it’s only gotten worse. Note, for example, the absurd layout of the dialog box above.

\n

Small Excel spreadsheets usually get moved over to Numbers, a considerably less powerful spreadsheet, but one that works the way a normal Mac app works. Large Excel spreadsheets get converted to CSV (often after a short visit to Numbers, as I’ve had trouble with Excel’s CSV conversion) so I can query and manipulate the data with Pandas. Why don’t I just open the Excel file in Pandas directly? Because the spreadsheets I get are so filled with cruft, read_excel can’t be trusted.

\n
\n

Often, though, the measurements are entered as text. This is done by prefixing the data with a single quote mark, e.g.,

\n
'2 5/16\n
\n

or maybe

\n
'2-5/16\n
\n

These need to be converted into a decimal representation before exporting to CSV, so Pandas will recognize them as numbers rather than strings.1

\n

To handle this situation, I created a Keyboard Maestro called Floatize. To use Floatize, I

\n
    \n
  • Select the column of data I want to convert.
  • \n
  • Copy it to the clipboard.
  • \n
  • Run Floatize, which converts the clipboard from a fractional representation to decimal.
  • \n
  • Paste the result back into the column.
  • \n
\n

Here’s the macro:

\n

\"Floatize

\n

It takes the clipboard, sends it to a script as standard input, and then puts the output back into the clipboard. Here’s the Python script it runs:

\n
python:\n 1:  #!/usr/bin/env python\n 2:  \n 3:  from fractions import Fraction\n 4:  import re\n 5:  import sys\n 6:  \n 7:  def floatize(s):  \n 8:    if s in ['', 'n/a', 'none']:\n 9:      return ''\n10:    else:\n11:      try:\n12:        num = float(s)\n13:      except ValueError:\n14:        t = re.sub(r'(.*\\d) *- *(\\d.*)', r'\\1 \\2', s)\n15:        if t[0] == '-':\n16:          num = -float(sum(Fraction(x) for x in t[1:].split(None, 1)))\n17:        else:\n18:          num = float(sum(Fraction(x) for x in t.split(None, 1)))\n19:      return \"{:f}\".format(num)\n20:    \n21:  for line in sys.stdin.read().splitlines():\n22:    item = line.strip().lower()\n23:    print floatize(item)\n
\n

The bulk of the fraction parsing is done by the fractions module in either Line 16 or Line 18. The rest of the script is an attempt to manipulate the oddball types of data I get into a format that fractions understands. Here’s what it can handle:

\n
    \n
  • Blank, n/a or none entries get turned into an empty string (Lines 8–9). Note that entries are converted to lower case in Line 22, before being passed to the floatize function, so this handles N/A and None, too.
  • \n
  • Entries already in decimal format get converted to a float (Line 12) and then output in decimal format (Line 19). This is basically a pass-through.
  • \n
  • Entries with a dash between the whole and fractional parts get the dash removed (Line 14).
  • \n
  • Negative entries (Line 15) have their whole and fractional parts added together with the sign reversed (Line 16), and then output in decimal format (Line 19).
  • \n
  • Positive entries have their whole and fractional parts added together (Line 18), and then output in decimal format (Line 19).
  • \n
  • Entries that don’t look like any of the above cause an error.
  • \n
\n

The macro works perfectly in Numbers and BBEdit, but not in Excel. For some reason, nothing happens to the clipboard if I run the Floatize macro while Excel is the active application. I can, however, copy the column of fractions, activate another app, run Floatize, switch back to Excel and paste the converted clipboard. Another example of Excel not acting like a proper Mac app and another reason I move data out of Excel as quickly as I can.

\n

Despite its inability to work inside Excel, this macro has been very handy over the past couple of weeks. I’ve used it to clean up several data sets that were loaded with fractions.

\n

By the way, I will block anyone who tweets that I wouldn’t have this problem if I used the metric system.

\n
\n

Update Nov 13, 2017 10:04 PM  \nThe Twitter consensus is my problem in using this macro in Excel stems from Excel’s clipboard dumbassery. It apparently uses a private clipboard instead of the system clipboard until you bring another application to the front. Hence the macro’s inability to convert the clipboard unless I switched away from Excel.

\n

Jimmy Hartington suggested this workaround:

\n
\n
\n@drdrang Excel does something weird with the clipboard as you noticed.
Try this. Copy in Excel. Press Escape. Run the macro.
\nBy pressing Escape I think Excel stops messing with the clipboard.\n
Jimmy Hartington (@jimmyhartington) Nov 12 2017 11:06 PM
\n
\n
\n

This works perfectly and pointed me in the direction to make a better version of the macro, one that does all the copying, converting, and pasting:

\n

\"New

\n

Note the conditional action that simulates the pressing of the Escape key only if Excel is the front application. This is necessary because Escape causes Numbers to deselect the column, which screws up the paste action at the end of the macro.

\n

With this macro I can now select the column that needs converting and just press ⌃⌥⌘F to do the conversion in a single step. Thanks, Jimmy!

\n
\n
\n
\n
    \n
  1. \n

    There are probably ways to get Pandas to do the conversion as it imports the data, but I feel more comfortable—more in control—if I do it ahead of time. ↩︎

    \n
  2. \n
\n

\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "A modest proposal", "url": "http://leancrew.com/all-this/2017/11/a-modest-proposal/", "author": {"name": "Dr. Drang"}, "summary": "MacStories should think about cleaning up some of its Apple sales charts.", "date_published": "2017-11-07T03:27:11+00:00", "id": "http://leancrew.com/all-this/2017/11/a-modest-proposal/", "content_html": "

With Apple sales and graphing on my mind, I’d like to make a small complaint and suggestion to the folks at MacStories: some of your quarterly graphs could use a little scrubbing; they’d be much easier on the eyes if they were cleaned up a bit.

\n

I’m talking about the ones that look like this:

\n

\"MacStories

\n

My complaint is with the clutter along the x-axis. That uniform gray mass of text detracts from the data above it. And although putting a label at each quarter seems to provide more information to the reader, the reader actually gets less out of it because it’s too hard to read. The axis would be improved if it had just yearly Q1 labels. Maybe even one label every other year. Fewer labels would mean they could be turned back horizontal, which would be easier to read and would give more vertical space for the data.

\n

I suspect every data point is labeled because that’s the way Numbers wants to do it, and MacStories’ automated system for generating these graphs follows the Numbers defaults. But if you were making these graphs by hand, you’d never label the x-axis this way. An automated system shouldn’t, either.

\n

On the Mac, Numbers has a way to skip labels on the x-axis by selecting Custom Category Intervals from the Category Labels popup menu and then choosing how often to put the labels.

\n

\"Mac

\n

Sadly, I can’t find that option in the iOS version. Maybe it’s tucked away in a less obvious place.

\n

\"iOS

\n

Personally, I’m not a fan of either version of Numbers when it comes to making graphs because I like way more control than they give. But Numbers (like most graphing programs and libraries) can make nice graphs if you’re willing to break away from its defaults.

\n

I don’t really want to pick on MacStories. Their graphs are as good as, if not better than, what you typically find in blogs and news sites. Most graphs, unfortunately, are based on the default settings of the software that made them, and come out looking a little clumsy.

\n

I read MacStories because it has stylish writing. I’d like its graphs to be just as stylish.

\n
\n

Update Nov 7, 2017 8:55 PM
\nI was informed on Twitter by two authoritative sources (this one and this one) that the MacStories graphs are made with Excel, not Numbers. I haven’t used Excel for graphing since the mid-90s, but even then it had more customization options than Numbers has now. So I’m crossing my fingers for cleaner x-axes in January’s graphs.

\n

\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "Apple sales graphs and the iPhone 7", "url": "http://leancrew.com/all-this/2017/11/apple-sales-graphs-and-the-iphone-7/", "author": {"name": "Dr. Drang"}, "summary": "Obligatory quarterly post.", "date_published": "2017-11-06T02:53:19+00:00", "id": "http://leancrew.com/all-this/2017/11/apple-sales-graphs-and-the-iphone-7/", "content_html": "

Last week Apple released its 2017 Q4 (which everyone else’s calendar says is 2017 Q3) sales and revenue figures, which means a boatload of graphs from all your fave rave Apple-oriented bloggers. I’m a few days late, as usual, despite having long ago written scripts to generate these graphs.

\n

I don’t make as many graphs as everyone else, partly because I’m lazy, but mainly because don’t care about revenue. My concern is the popularity of the devices I use, not how expensive they are. I want third-party developers to keep writing apps for them, and that means there has to be a market for them out there.

\n

Here’s the summary graph, with the sales figures for the three main Apple products. The dots are the quarterly sales and the lines are the four-quarter moving averages.

\n

\"Apple

\n

The iPhone dominates the scale of this graphs, so it’s helpful to also see the iPad and Mac on their own graphs.

\n

\"iPad

\n

\"Mac

\n

The good news is the second straight quarter of year-over-year increase in iPad sales after its long, well-documented slide since 2013. Additional good news is the Mac’s slow but consistent rise over the past year—four straight quarters of year-over-year sales increases. Imagine how much better the figures would be if people really liked the Touch Bar.

\n

I think its fair to say, though, that the iPhone 7 was something of a dud. It had a good start, but its non-intro quarters had almost exactly the same sales as the 6S had. Look at the final three quarters of each.

\n

\"Apple

\n

As I’ve argued before, the iPhone 6S sales looked lackluster only in comparison to the gangbusters popularity of the iPhone 6. If one ignored the year of the 6, the 6S’s sales were more or less on the same upward trend as the previous three editions. The iPhone 7 was on that same trend for its first quarter but went dead flat after that.

\n

Apple’s report in January will be very interesting.


\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "Another one-off Keyboard Maestro macro", "url": "http://leancrew.com/all-this/2017/11/another-one-off-keyboard-maestro-macro/", "author": {"name": "Dr. Drang"}, "summary": "I should do more of these.", "date_published": "2017-11-03T02:02:41+00:00", "id": "http://leancrew.com/all-this/2017/11/another-one-off-keyboard-maestro-macro/", "content_html": "

Do you use Keyboard Maestro (or AppleScript or whatever) for one-time, throwaway macros as often as you should? I know I don’t, but I did put one together a couple of days ago and used a feature I’d never tried before.

\n

I had one of those recalcitrant PDFs that I often get from clients. This one was 25–30 pages long, each an E-sized floor plan drawing for a building. The drawings were all black-and-white, but the PDF had color annotations added. I needed to add my own annotations to most of the pages, but something about the format of the file made it very cumbersome to work with. I tried Preview, PDF Expert, and PDfpen Pro, and they all were glacially slow when panning, zooming, and switching pages.

\n

So I broke the file up into individual pages using PDFtk:

\n
pdftk drawings.pdf burst\n
\n

The single-page files didn’t make me wait for the spinning beach ball, so I was able to add my annotations quickly in PDF Expert. Then came an impasse.

\n

I wanted to email the drawings with my annotations back to the client and to some other parties, but they were too big to fit in an email. I could use multiple emails, but that’s a recipe for losing some of the files. I could use a Dropbox link, but I had a sense that one of the other parties wouldn’t understand how that worked. What seemed best was to convert the files to JPEGs at the lowest legible resolution, zip them together, and send the zipped file in a single email.1

\n

My normal practice would be to use sips for this, because I can issue a single command to convert any number of files. But I soon learned that sips doesn’t handle annotations properly when converting the format of a file from PDF to JPEG. In my brief testing, I found that neither my annotations or the ones that came from the client were visible in the converted JPEGs.

\n

Preview, though, can export a PDF as a JPEG with the annotations intact and visible. Which presumably means that sips and Preview are using different code bases for the conversion. Whatever.

\n

The problem with using Preview is I’d have to convert every file by hand. Not the most burdensome job I’ve ever had, but one that’s boring and susceptible to error. Enter Keyboard Maestro.

\n

Here’s the macro that exports the current file in Preview to a JPEG and closes it:

\n

\"Convert

\n

You’ll note there’s no step for setting the resolution for the exported file. That’s because once it’s set, it doesn’t change from one export to the next.

\n

I didn’t try to have the macro open each PDF in turn, because I didn’t trust myself to do that right. I just opened all of them and then ran the macro by pressing ⌃⌥⌘J repeatedly. The macro closed each file after exporting it, leaving the next window ready to be operated on.

\n

The new (for me) thing with this macro was the “and drag to” part at the bottom of the third step. That’s what moved the popup menu selection from PDF up to JPEG.

\n

\"Preview

\n

I figured out how much to drag by taking a screenshot like the one above and using a selection in Acorn to measure the vertical distance from the center of PDF to the center of JPEG. No trial and error.

\n

With 25–30 files to convert, it’s possible I did save time with this macro. But the main reason I made it was to avoid the tedium and the likelihood of error on my part. These are not independent—I’m far more prone to make errors when the task is repetitive and doesn’t maintain my attention.

\n
\n
\n
    \n
  1. \n

    No, a Dropbox link is really no more complicated than a zip file, but zip files are more familiar to more people. And although zipping JPEGs doesn’t make them smaller, it does package them up in a way that naive Windows users are usually comfortable with. It would be lovely if I always worked with people whose computer skills were trustworthy, but that’s not the world I live in. ↩︎

    \n
  2. \n
\n

\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "Binomial baseball", "url": "http://leancrew.com/all-this/2017/10/binomial-baseball/", "author": {"name": "Dr. Drang"}, "summary": "Teams with a 3–2 lead in the World Series win less often than you might think.", "date_published": "2017-10-30T18:14:47+00:00", "id": "http://leancrew.com/all-this/2017/10/binomial-baseball/", "content_html": "

While reading a recap of last night’s World Series games, I saw this statistic: of the 65 Series that have had a sixth game, the team with the 3–2 lead has won the Series 43 times. This was, I think, intended to show us that the Astros have a strong chance to beat out the Dodgers for the title. And they do. But not as strong as you might expect.

\n

If the teams were evenly balanced and each game independent of the others, we would expect the team with the 3–2 lead to win 75% of the time. 50% of the time they’d win the sixth game and the Series would be over; 25% of the time (50% of the other 50%) they’d win in the seventh game. So the leading team “should” have won the Series 48 or 49 times out of 65, not 43 times.

\n

Is this 5 or 6 game difference meaningful? For that we need to do some calculations using the binomial distribution. Python’s SciPy set of libraries has a subsection of statistical modules, including one for binomial distribution calculations. We can import it this way:

\n
python:\nfrom scipy.stats import binom\n
\n

Let’s start by figuring out the probability that the leading team would win 43 times in 65 trials. With a 75% probability of winning the Series in each trial, the probability of 43 Series wins in 65 chances is calculated through

\n
python:\nbinom.pmf(43, 65, .75)\n
\n

where the pmf function gets its name from the standard abbreviation for “probability mass function.” The answer is 0.029 or just under 3%. This makes it seem very unlikely that our assumption of 50–50 games would lead to only 43 Series wins for the leading team.

\n

But that isn’t the way these sorts of calculations are normally done. If we want to find out if a seemingly out-of-whack result is “statistically significant,” we should look at the probability of results that are at least as far away from our expectations as the actual result was. In our case, that means looking not only at the probability of 43 Series wins out of 65 chances, but also 42 wins, 41 wins, and so on. We then add up all of these “at least as weird” probabilities.

\n

The usual terminology for this sort of summation is “cumulative distribution function,” and the binom module has a function for it:

\n
python:\nbinom.cdf(43, 65, .75)\n
\n

The result is 0.0695, or about 7%. Another way of looking at this is that if our assumption of 50–50 games were correct, there’s a 93% chance that the leading team would win the Series more than 43 times in 65 chances.

\n

In hypothesis testing, the value 0.0695 is called the p-value,\nand it’s common in many fields to consider a result statistically significant if its p-value is less than 0.05. Using that criterion, we would not take the difference between our “null hypothesis” of 50–50 games and the World Series history as statistically significant.1

\n

But it’s something for Dodgers fans to cling to.

\n
\n
\n
    \n
  1. \n

    Yes, I’ve been a little breezy here with my definition of null and alternate hypotheses and one-sided vs. two-sided rejection areas, but it’s just baseball. ↩︎

    \n
  2. \n
\n

\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "Judas", "url": "http://leancrew.com/all-this/2017/10/judas/", "author": {"name": "Dr. Drang"}, "summary": "I don't believe you.", "date_published": "2017-10-28T14:33:47+00:00", "id": "http://leancrew.com/all-this/2017/10/judas/", "content_html": "

I can’t say I wasn’t warned. The concert’s promotional artwork (especially those fonts) and the previous two albums, Shadows in the Night and Fallen Angels, were strong clues that I was going to be seeing a Bob Dylan infected by the Great American Songbook.

\n

\"Dylan

\n

So yes, we got “Melancholy Mood” and “Autumn Leaves” and “The September of My Years” with Bob crooning into a big microphone and holding its stand at a jaunty angle. But that’s not what I disliked about the concert. In fact, those songs were done pretty well, and Dylan’s voice sounded better than any previous concert I’ve been to. It was what he did to his own songs that was frustrating.

\n

No one in his right mind goes to a Dylan concert expecting to hear his classics played or sung the way they were done originally. But the treatment they’re given is always interesting and usually fun. Not this time. Apart from “It Ain’t Me, Babe” and “Highway 61 Revisited,” the Dylan songs suffered from arrangements that were, I guess, meant to match the style of the non-Dylan songs. Under these conditions, you can’t expect the songs to rock, but you can expect them to swing. And the band just didn’t swing.

\n

There was a sameness to Charlie Sexton’s guitar work throughout the night. Despite changing instruments several times, his sound was constrained and repetitive. But when a band doesn’t swing, it’s mainly because of the rhythm section. These are talented musicians who can play in any style, so if they didn’t find a groove it must have been because Bob didn’t want them to.

\n

As a result, he lost the audience. Many left early and those who hung in there stayed glued to their seats until the encores. Can you imagine a Dylan audience not standing to “Tangled Up in Blue”?

\n

The optimist in me notes that Dylan never sticks with anything for long, and after two albums of standards he may be ready for the next thing. I hope he comes back to the notion that that it’s his songs, and the blues and country/folk they came out of, that are truly the Great American Songbook and gives them the treatment they deserve.


\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "Icons", "url": "http://leancrew.com/all-this/2017/10/icons/", "author": {"name": "Dr. Drang"}, "summary": "Brett Terpstra's little app has a strangely familiar icon.", "date_published": "2017-10-27T01:39:06+00:00", "id": "http://leancrew.com/all-this/2017/10/icons/", "content_html": "

I have some code sitting around here somewhere for extracting the images (app icons, book covers, album art, etc.) from the various iTunes and App Stores. But I think it’ll be easier to use Brett Terpstra’s iTunesIcon Automator app, which just got an update.

\n

Apart from its appeal to my laziness, Brett’s little app has a cool icon that I took an instant liking to.

\n

\"iTunesIcon

\n

If you work around industrial and construction equipment, Brett’s icon should bring a smile of familiarity. It’s highly reminiscent of the hands in safety signs and decals that are in dire straits.

\n

\"Safety

\n

Images from SafetySign.com

\n

I’m sure your fingers are safe when using iTunesIcon.


\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "Feed reading", "url": "http://leancrew.com/all-this/2017/10/feed-reading/", "author": {"name": "Dr. Drang"}, "summary": "My feed reading setup is basically complete", "date_published": "2017-10-23T00:10:42+00:00", "id": "http://leancrew.com/all-this/2017/10/feed-reading/", "content_html": "

Gabe Weatherhead has a nice article today on how he uses Feedbin to handle his reading on the web. If you follow Gabe—and if not, why don’t you?—it will not surprise you to learn that he’s formidably organized. I don’t see myself following in his footsteps, but it’s always useful to learn how smart people do things. His article also reminded me that I’ve been meaning to write about my feed reading setup.

\n

A couple of years ago, I let my subscription to Feed Wrangler lapse and started using a homemade, web-based RSS reading system. The heart of the system is still the script described in this post, but with a some changes as I thought of better ways of doing things.

\n

The biggest change came in the past few months. Initially, I created a single static web page with all of today’s articles from the feeds I subscribe to. A cron task updated the page a few times an hour throughout the day. In this system, “today” was defined as “from 10:00 pm last night until now,” and the page would grow in size from morning to night.

\n

The advantage of this temporal arrangement from a programming point of view was that I didn’t have to write any code to keep track of whether I’d already read an article or not, and there were no external dependencies. If it was published “today,” it got on the page.

\n

The disadvantage was from the reading point of view. As I visited the page throughout the day, it became more and more filled with article I’d already read. This wasn’t as terrible as you might think. The articles were arranged in reverse chronological order by publication time, so the ones I’d read were typically at the bottom of the page. I say “typically” because some feeds—XKCD comes to mind—are very bad at providing accurate publication times and their articles would sometimes end up at the bottom despite being recently published.

\n

Eliminating the reading disadvantage meant keeping track of what I’d read and showing only what I hadn’t—eliminating the programming advantage. I decided to keep track of read articles in an SQLite database and to add items to that database through a button placed at the bottom of each article on my RSS page.

\n

\"RSS

\n

This meant

\n
    \n
  1. Building a database that would uniquely identify every article. This was pretty simple. Each record has just two fields: the name of the website and the unique article ID (which is often just the article’s URL but is sometimes a long alphanumeric string generated by the site’s blogging software).
  2. \n
  3. Altering the existing script that builds the RSS page to filter out feed items that are in the database. Because Python has an SQLite module as part of its standard library and the syntax of SQL commands is straightforward, this wasn’t as tricky as I thought it would be. In fact, the new code is easier to read than the time-based filtering code I removed.
  4. \n
  5. Writing a server script (basically just a CGI script) to add an article to the database when given the blog name and article ID via the POST method. It’s been a while since I last wrote a CGI script, but it was like riding a bicycle.
  6. \n
  7. Adding some JavaScript with XMLHttpRequest to the RSS page to call the server script when a button is pressed. This took the most time, mainly because everyone in the world (except me) knows how to do AJAX now, and finding references written at an appropriately low level was harder than I expected. I found this Stack Overflow discussion helpful.
  8. \n
\n

So now I usually tap the Mark as read button when I get to the end of an article. If it’s a long article that I want to read later, I don’t mark it as read, and it’ll be there the next time I bring up the RSS page.

\n

Fearing I’d forget how to use XMLHttpRequest, I quickly included another form at the end of each article for adding that article to my Pinboard account. I didn’t bother adding labels to the text field, because I’m the only one who uses this and I know the field is for tags. I did, however, include some DOM stuff to make it obvious when I’d marked an article as read or added it to Pinboard.

\n

\"RSS

\n

What I like about this system is how portable and (I hope) future-proof it is. I’ve been reluctant to sign on with Feedbin or Feedly or BazQux or any of the other Google Reader replacements because I worry they’ll write a Medium post and disappear with my subscription list and whatever organization scheme I’ve created. My system can run on any web server with Python, SQLite, and a cgi-bin directory. I think that’ll mean “any server, anywhere” for a very long time.


\n

[If the formatting looks odd in your feed reader, visit the original article]

"}], "home_page_url": "http://leancrew.com/all-this/", "version": "https://jsonfeed.org/version/1", "icon": "http://leancrew.com/all-this/resources/snowman-200.png"} diff --git a/Frameworks/RSParser/RSParserTests/Resources/coco.html b/Frameworks/RSParser/RSParserTests/Resources/coco.html deleted file mode 100644 index c17abb0d4..000000000 --- a/Frameworks/RSParser/RSParserTests/Resources/coco.html +++ /dev/null @@ -1,2329 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - Review: 'Coco' Is Among Pixar's Best Movies in Years - The Atlantic - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - - - - - - -
- - - - - - - - - - - - - - - - - - -
- - -
-
-
-

Coco Is Among Pixar's Best Movies in Years

- -

Full of wit, music, and color, this Día de Muertos–themed tribute to family marks a return to form for the studio.

- -
-
- - - -
-
- - A still from Pixar's new film 'Coco' - - - - - - -
- -
- -
- - -
- - - - - - - - - - - - - -
-
- - - - - - - - - - - - - -
- - - - - - -
-
-
- - - -
- -
- - -

Well, that’s more like it. As someone who has written at some length about the decline of Pixar Studios since its acquisition by Disney, I am especially pleased to be proven wrong, even if only intermittently. The studio’s latest release, Coco, is one such occasion.

Though Pixar has never acknowledged as much publicly, its cinematic philosophy (and business model) has shifted notably: Where the studio once aspired to excellence with every single picture—Pixar President Ed Catmull wrote an entire book expressing this ideal, Creativity Inc.—it now seems content to roll out a few profitable, hyper-merchandise-friendly sequels for every genuinely original feature it unveils. (To put it another way, the studio has shifted away from “creativity” and toward “inc.”)

But if Finding Dory and Cars 3 are the price we must pay for a film such as Coco, then so be it. Pixar’s latest is up there with Inside Out among the studio’s best features in years—less complex than Pete Docter’s 2015 film, but perhaps a tad more emotionally resonant.

Miguel Rivera (Anthony Gonzalez) is a 12-year-old boy in Mexico whose greatest desire in life is to be a musician like his idol, the mid-century legend Ernesto de la Cruz (Benjamin Bratt). Alas, Miguel’s great-great-grandmother was abandoned by her musician husband, and the Rivera family has enforced an iron-clad policy against music ever since. Instead, each subsequent generation has gone into the family business of making shoes. (Shades of Hermey, the toy-making elf who wished to become a dentist.)

But could it be that de la Cruz was in fact Miguel’s long since written-off great-great-grandfather? That certainly appears to be the case. So in order to participate in a music competition on Día de Muertos, Miguel “borrows” de la Cruz’s famous guitar, his own having been smashed earlier in the day by his grandmother. But with the very first strum, Miguel is transported to the Land of the Dead. There, he meets departed members of his own family and ultimately, with the help of a trickster named Héctor (Gael García Bernal), de la Cruz himself.

Directed by longtime Pixarian Lee Unkrich (Toy Story 3), the tale that unfolds from these beginnings is not terribly innovative (less so, for instance, than 2014’s similarly themed though less well-realized The Book of Life). But it is a tale told with considerable wit—this is one of Pixar’s funniest films—and genuine tenderness. There are a few nice twists and reversals along the way. And while the movie’s conclusion is not difficult to see coming, anyone whose heart is not warmed by it may wish to consult with an cardio-therapist.

Befitting its subject, this is the most musical feature yet produced by Pixar, with songs co-written by Robert Lopez, of The Book of Mormon, Avenue Q, and Frozen fame. There are clever pop-cultural nuggets scattered throughout: a Mac Plus that is condemned as a “devil box” and smashed with a shoe; a gatehouse between the lands of the living and the dead that bears a distinct resemblance to the entrance to Disneyland; a hilariously avant-garde stage show put on by a deceased Frida Kahlo.

But where Coco shines most brightly—literally—is in its vibrant visuals, which rely on a palette of fluorescent greens, blues, yellows, and oranges. In this telling, the Land of the Dead is not a fearsome place, but rather a never-ending skeleton party conducted in a glorious multi-tiered city that rises from sea-level houseboats to vast, imperious towers inhabited by celebrities such as de la Cruz—all of them connected by arched bridges and aerial trams.

Does Coco rise to the heights of Pixar’s very best work? No. But it is a generous, heartfelt film, full of color and music, one that offers a timely Thanksgiving tribute to the intergenerational importance of family. Its very title lovingly derives from the name of Miguel’s oldest living relative, great-grandmother Mamá Coco, whose importance to the story only becomes clear late in the film.

I wish the movie suggested that all was now well with Pixar, but warning signs are, if anything, multiplying. The studio’s next two films will be sequels, The Incredibles 2 and an utterly heretical “franchise reboot,” Toy Story 4. With Coco, even the customary delight of a Pixar short before the movie is missing: In its place is “Olaf’s Frozen Adventure,” which whatever its quality (it did not screen for critics) suggests that the studio is being ever-more subsumed into its Disney parent. And the interlocking news stories that Pixar guru John Lasseter is going on leave due to alleged inappropriate behavior and that Rashida Jones left Toy Story 4 over issues of diversity are depressing on almost every level imaginable.

But those are issues to be addressed in the days to come. In the meantime, my advice is to round up the family, take them to Coco, and together give thanks.

- -
- -
- - - - - - - - -
-
- - - - - - - - - - - -
- - - - - - - - - - - - -
-

About the Author

- - - -
    - -
  • - - - Christopher Orr - -
    - - Christopher Orr is a senior editor and the principal film critic at The Atlantic. He has written on movies for the New Republic, LA Weekly, Salon, and The New York Sun, and has worked as an editor for numerous publications. - - - -
    - - - - -
  • - -
- -
- -
-
-
- - - -
- -
- - - - - - - - - - - - - -
- - - - - -
- - - - - - - -
- - - - - - - - - - - - - - - - - -
- - - - - - - - - - -
- -
- - -
- - -
-
- - - -
- - - - -
- - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Frameworks/RSParser/RSParserTests/Resources/curt.json b/Frameworks/RSParser/RSParserTests/Resources/curt.json deleted file mode 100644 index 44a0686a4..000000000 --- a/Frameworks/RSParser/RSParserTests/Resources/curt.json +++ /dev/null @@ -1 +0,0 @@ -{"icon":"http:\/\/curtclifton.net\/style\/feedicon.png","user_comment":"This feed allows you to read the posts from this site in any feed reader that supports the JSON Feed format. To add this feed to your reader, copy the following URL — http:\/\/curtclifton.net\/feed.json — and add it your reader.","favicon":"http:\/\/curtclifton.net\/style\/touch-icon.png","description":"Full-text posts, generally related to software development on Apple’s platforms. Programming language geekery.","version":"https:\/\/jsonfeed.org\/version\/1","title":"curtclifton.net","items":[{"date_published":"2018-01-06T08:00","title":"Twitter Quitter","id":"twitter-quitter","content_html":"

I’ve decided to close my Twitter account. William Van Hecke makes a convincing case<\/a> for its diminishing utility, and it’s clear that Jack<\/a> is more concerned with eyeballs than standards<\/a>.<\/p>\n\n

I stopped regularly reading my timeline months ago. The few times I have dipped in, I’ve ended up angry or depressed. Despite occasional bright spots, there is always someone sharing the angst of the day. I read the news. I don’t need Twitter to make me more anxious. As such, I’ve only been using Twitter for cross posting from my micro.blog account<\/a> and responding to mentions. Slack<\/a> meets my social chat needs without the screaming-into-the-void that Twitter has become.<\/p>\n\n

After some reflection, I’ve concluded that even posting to Twitter is just providing content to a platform for hate and anger. I can’t fix that problem, but I can stop contributing to the platform. And so I will.<\/p>\n\n

I’m taking my Twitter account private. I’ll stop reading and (after this) posting to it. If you want to get in touch, please email<\/a>, iMessage, or drop a mention <\/span>@<\/span>curt<\/span><\/code> on micro.blog<\/a>. I’d also be happy for an invite to your Slack group or a friend request on Facebook<\/a>.<\/em> (While Facebook is also an addiction-exploiting attention hole, it provides much more control to users. The positives there outweigh the negatives.)<\/p>\n\n

Be well. Find the good in the world. Peace.<\/p>\n","url":"http:\/\/www.curtclifton.net\/twitter-quitter"},{"date_published":"2017-12-15T08:00","title":"Next Actions","id":"next-actions","content_html":"

After six and a half wonderful years, today is my last with the Omni Group. It’s been the joy and privilege of a lifetime to work with the great people at Omni. Care for others permeates the culture at Omni, from interpersonal interactions, to software design, to our amazing Support Humans. It’s been especially rewarding to contribute to OmniFocus<\/a>, an app that’s been invaluable to me personally and that helps many others achieve their goals.<\/p>\n\n

While it’s difficult to say goodbye to all that, I have an opportunity to join a small fruit company in Cupertino working on iPad software for education. The role combines several of my passions: teaching, learning, mentoring, and building elegant software. I’m looking forward to joining my new team in January and making great things together.<\/p>\n\n

I’ll miss the amazing Xcoders<\/a> community, but hope to make it back occasionally. And, of course, I’ll see you all when you come to San Jose for WWDC and related festivities. <\/p>\n\n

It will be great to be able spend more time with friends in the Bay Area. The next few weeks will be a whirlwind with the holidays and moving, but if you’re in the area hit me up<\/a> in the new year and let’s get together.<\/p>\n","url":"http:\/\/www.curtclifton.net\/next-actions"},{"date_published":"2017-10-27T07:00","title":"These are a Few of My Stateful Machines","id":"these-are-a-few-of-my-stateful-machines","content_html":"

I’m excited to be presenting at the inaugural Swift by Northwest<\/a> conference today. My talk is on state machines and how easy they are to implement in Swift.<\/p>\n\n