From 6f1164f5ac94ddc13a63731818bd20e35b56e827 Mon Sep 17 00:00:00 2001 From: Frank Courville Date: Sun, 9 Apr 2017 17:25:01 -0400 Subject: [PATCH] Initial commit --- .gitignore | 133 + TweetSample.xcodeproj/project.pbxproj | 443 ++++ TweetSample/AppDelegate.swift | 39 + .../AppIcon.appiconset/Contents.json | 68 + .../Base.lproj/LaunchScreen.storyboard | 27 + TweetSample/Colors.swift | 15 + TweetSample/ComposeTextViewController.swift | 86 + TweetSample/Fonts.swift | 30 + TweetSample/HashtagsDataSource.swift | 33 + TweetSample/Info.plist | 54 + TweetSample/Montserrat-Bold.ttf | Bin 0 -> 29560 bytes TweetSample/Montserrat-Regular.ttf | Bin 0 -> 29016 bytes TweetSample/NewTweet.swift | 34 + TweetSample/OFL.txt | 92 + .../SelectHashtagsViewController.swift | 53 + TweetSample/SelectPhotoViewController.swift | 105 + TweetSample/Tweet.swift | 48 + TweetSample/TweetCell.swift | 62 + TweetSample/TweetComposeCoordinator.swift | 85 + TweetSample/TweetDetailViewController.swift | 99 + TweetSample/TweetListCoordinator.swift | 52 + TweetSample/TweetListDataSource.swift | 43 + TweetSample/TweetListViewController.swift | 45 + TweetSample/timeline.json | 2218 +++++++++++++++++ 24 files changed, 3864 insertions(+) create mode 100644 .gitignore create mode 100644 TweetSample.xcodeproj/project.pbxproj create mode 100644 TweetSample/AppDelegate.swift create mode 100644 TweetSample/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 TweetSample/Base.lproj/LaunchScreen.storyboard create mode 100644 TweetSample/Colors.swift create mode 100644 TweetSample/ComposeTextViewController.swift create mode 100644 TweetSample/Fonts.swift create mode 100644 TweetSample/HashtagsDataSource.swift create mode 100644 TweetSample/Info.plist create mode 100755 TweetSample/Montserrat-Bold.ttf create mode 100755 TweetSample/Montserrat-Regular.ttf create mode 100644 TweetSample/NewTweet.swift create mode 100755 TweetSample/OFL.txt create mode 100644 TweetSample/SelectHashtagsViewController.swift create mode 100644 TweetSample/SelectPhotoViewController.swift create mode 100644 TweetSample/Tweet.swift create mode 100644 TweetSample/TweetCell.swift create mode 100644 TweetSample/TweetComposeCoordinator.swift create mode 100644 TweetSample/TweetDetailViewController.swift create mode 100644 TweetSample/TweetListCoordinator.swift create mode 100644 TweetSample/TweetListDataSource.swift create mode 100644 TweetSample/TweetListViewController.swift create mode 100644 TweetSample/timeline.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b9c3b2e --- /dev/null +++ b/.gitignore @@ -0,0 +1,133 @@ + +# Created by https://www.gitignore.io/api/vim,macos,swift,xcode + +### macOS ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Swift ### +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## Build generated +build/ +DerivedData/ + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ + +## Other +*.moved-aside +*.xccheckout +*.xcscmblueprint + +## Obj-C/Swift specific +*.hmap +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +# Package.pins +.build/ + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# Pods/ + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output + +### Vim ### +# swap +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-v][a-z] +[._]sw[a-p] +# session +Session.vim +# temporary +.netrwhist +*~ +# auto-generated tag files +tags + +### Xcode ### +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## Build generated + +## Various settings + +## Other + +### Xcode Patch ### +*.xcodeproj/* +!*.xcodeproj/project.pbxproj +!*.xcodeproj/xcshareddata/ +!*.xcworkspace/contents.xcworkspacedata +/*.gcno + +# End of https://www.gitignore.io/api/vim,macos,swift,xcode diff --git a/TweetSample.xcodeproj/project.pbxproj b/TweetSample.xcodeproj/project.pbxproj new file mode 100644 index 0000000..24ffb2c --- /dev/null +++ b/TweetSample.xcodeproj/project.pbxproj @@ -0,0 +1,443 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 4140EA411E27F8B900F448F7 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4140EA401E27F8B900F448F7 /* AppDelegate.swift */; }; + 4140EA481E27F8B900F448F7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4140EA471E27F8B900F448F7 /* Assets.xcassets */; }; + 4140EA4B1E27F8B900F448F7 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4140EA491E27F8B900F448F7 /* LaunchScreen.storyboard */; }; + 4140EA551E27F95700F448F7 /* TweetListCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4140EA541E27F95700F448F7 /* TweetListCoordinator.swift */; }; + 4140EA571E27FA0400F448F7 /* TweetListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4140EA561E27FA0400F448F7 /* TweetListViewController.swift */; }; + 4140EA5A1E27FB2600F448F7 /* Tweet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4140EA591E27FB2600F448F7 /* Tweet.swift */; }; + 4140EA5C1E27FDE900F448F7 /* TweetListDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4140EA5B1E27FDE900F448F7 /* TweetListDataSource.swift */; }; + 4140EA5E1E27FF7A00F448F7 /* TweetCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4140EA5D1E27FF7A00F448F7 /* TweetCell.swift */; }; + 418830F41E2810390020F89F /* timeline.json in Resources */ = {isa = PBXBuildFile; fileRef = 418830F31E2810390020F89F /* timeline.json */; }; + 418830F81E292E880020F89F /* TweetDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 418830F71E292E880020F89F /* TweetDetailViewController.swift */; }; + 418830FA1E293BB00020F89F /* TweetComposeCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 418830F91E293BB00020F89F /* TweetComposeCoordinator.swift */; }; + 418830FF1E2941370020F89F /* SelectHashtagsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 418830FE1E2941370020F89F /* SelectHashtagsViewController.swift */; }; + 418831011E29424C0020F89F /* ComposeTextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 418831001E29424C0020F89F /* ComposeTextViewController.swift */; }; + 418831031E2943B20020F89F /* NewTweet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 418831021E2943B20020F89F /* NewTweet.swift */; }; + 418831051E294C1C0020F89F /* SelectPhotoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 418831041E294C1C0020F89F /* SelectPhotoViewController.swift */; }; + 41AA37F91E2D1C21005D5888 /* HashtagsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41AA37F81E2D1C21005D5888 /* HashtagsDataSource.swift */; }; + 41F223B21E9976FA005BC4B6 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41F223B11E9976FA005BC4B6 /* Colors.swift */; }; + 41F223B41E9978E2005BC4B6 /* Fonts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41F223B31E9978E2005BC4B6 /* Fonts.swift */; }; + 41F223B81E9979A9005BC4B6 /* Montserrat-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 41F223B51E9979A9005BC4B6 /* Montserrat-Bold.ttf */; }; + 41F223B91E9979A9005BC4B6 /* Montserrat-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 41F223B61E9979A9005BC4B6 /* Montserrat-Regular.ttf */; }; + 41F223BA1E9979A9005BC4B6 /* OFL.txt in Resources */ = {isa = PBXBuildFile; fileRef = 41F223B71E9979A9005BC4B6 /* OFL.txt */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 4140EA3D1E27F8B900F448F7 /* TweetSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TweetSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 4140EA401E27F8B900F448F7 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 4140EA471E27F8B900F448F7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 4140EA4A1E27F8B900F448F7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 4140EA4C1E27F8B900F448F7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 4140EA541E27F95700F448F7 /* TweetListCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TweetListCoordinator.swift; sourceTree = ""; }; + 4140EA561E27FA0400F448F7 /* TweetListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TweetListViewController.swift; sourceTree = ""; }; + 4140EA591E27FB2600F448F7 /* Tweet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tweet.swift; sourceTree = ""; }; + 4140EA5B1E27FDE900F448F7 /* TweetListDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TweetListDataSource.swift; sourceTree = ""; }; + 4140EA5D1E27FF7A00F448F7 /* TweetCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TweetCell.swift; sourceTree = ""; }; + 418830F31E2810390020F89F /* timeline.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = timeline.json; sourceTree = ""; }; + 418830F71E292E880020F89F /* TweetDetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TweetDetailViewController.swift; sourceTree = ""; }; + 418830F91E293BB00020F89F /* TweetComposeCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TweetComposeCoordinator.swift; sourceTree = ""; }; + 418830FE1E2941370020F89F /* SelectHashtagsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectHashtagsViewController.swift; sourceTree = ""; }; + 418831001E29424C0020F89F /* ComposeTextViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ComposeTextViewController.swift; sourceTree = ""; }; + 418831021E2943B20020F89F /* NewTweet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewTweet.swift; sourceTree = ""; }; + 418831041E294C1C0020F89F /* SelectPhotoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectPhotoViewController.swift; sourceTree = ""; }; + 41AA37F81E2D1C21005D5888 /* HashtagsDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HashtagsDataSource.swift; sourceTree = ""; }; + 41F223B11E9976FA005BC4B6 /* Colors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = ""; }; + 41F223B31E9978E2005BC4B6 /* Fonts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Fonts.swift; sourceTree = ""; }; + 41F223B51E9979A9005BC4B6 /* Montserrat-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Montserrat-Bold.ttf"; sourceTree = ""; }; + 41F223B61E9979A9005BC4B6 /* Montserrat-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Montserrat-Regular.ttf"; sourceTree = ""; }; + 41F223B71E9979A9005BC4B6 /* OFL.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = OFL.txt; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 4140EA3A1E27F8B800F448F7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4140EA341E27F8B800F448F7 = { + isa = PBXGroup; + children = ( + 4140EA3F1E27F8B900F448F7 /* TweetSample */, + 4140EA3E1E27F8B900F448F7 /* Products */, + ); + sourceTree = ""; + }; + 4140EA3E1E27F8B900F448F7 /* Products */ = { + isa = PBXGroup; + children = ( + 4140EA3D1E27F8B900F448F7 /* TweetSample.app */, + ); + name = Products; + sourceTree = ""; + }; + 4140EA3F1E27F8B900F448F7 /* TweetSample */ = { + isa = PBXGroup; + children = ( + 418830F21E28102E0020F89F /* Resources */, + 4140EA581E27FB1D00F448F7 /* Model */, + 4140EA531E27F91700F448F7 /* View Controllers */, + 4140EA521E27F90D00F448F7 /* Coordinators */, + 4140EA401E27F8B900F448F7 /* AppDelegate.swift */, + 4140EA471E27F8B900F448F7 /* Assets.xcassets */, + 4140EA491E27F8B900F448F7 /* LaunchScreen.storyboard */, + 4140EA4C1E27F8B900F448F7 /* Info.plist */, + ); + path = TweetSample; + sourceTree = ""; + }; + 4140EA521E27F90D00F448F7 /* Coordinators */ = { + isa = PBXGroup; + children = ( + 4140EA541E27F95700F448F7 /* TweetListCoordinator.swift */, + 418830F91E293BB00020F89F /* TweetComposeCoordinator.swift */, + ); + name = Coordinators; + sourceTree = ""; + }; + 4140EA531E27F91700F448F7 /* View Controllers */ = { + isa = PBXGroup; + children = ( + 418830FD1E29411D0020F89F /* Select Hashtags */, + 418830FC1E29410D0020F89F /* Select Photo */, + 418830FB1E2941010020F89F /* Compose Text */, + 418830F61E292E220020F89F /* Tweet Detail */, + 418830F51E29221A0020F89F /* Tweet List */, + ); + name = "View Controllers"; + sourceTree = ""; + }; + 4140EA581E27FB1D00F448F7 /* Model */ = { + isa = PBXGroup; + children = ( + 4140EA591E27FB2600F448F7 /* Tweet.swift */, + 418831021E2943B20020F89F /* NewTweet.swift */, + ); + name = Model; + sourceTree = ""; + }; + 418830F21E28102E0020F89F /* Resources */ = { + isa = PBXGroup; + children = ( + 41F223BB1E9979AF005BC4B6 /* Fonts */, + 418830F31E2810390020F89F /* timeline.json */, + 41F223B11E9976FA005BC4B6 /* Colors.swift */, + 41F223B31E9978E2005BC4B6 /* Fonts.swift */, + ); + name = Resources; + sourceTree = ""; + }; + 418830F51E29221A0020F89F /* Tweet List */ = { + isa = PBXGroup; + children = ( + 4140EA561E27FA0400F448F7 /* TweetListViewController.swift */, + 4140EA5B1E27FDE900F448F7 /* TweetListDataSource.swift */, + 4140EA5D1E27FF7A00F448F7 /* TweetCell.swift */, + ); + name = "Tweet List"; + sourceTree = ""; + }; + 418830F61E292E220020F89F /* Tweet Detail */ = { + isa = PBXGroup; + children = ( + 418830F71E292E880020F89F /* TweetDetailViewController.swift */, + ); + name = "Tweet Detail"; + sourceTree = ""; + }; + 418830FB1E2941010020F89F /* Compose Text */ = { + isa = PBXGroup; + children = ( + 418831001E29424C0020F89F /* ComposeTextViewController.swift */, + ); + name = "Compose Text"; + sourceTree = ""; + }; + 418830FC1E29410D0020F89F /* Select Photo */ = { + isa = PBXGroup; + children = ( + 418831041E294C1C0020F89F /* SelectPhotoViewController.swift */, + ); + name = "Select Photo"; + sourceTree = ""; + }; + 418830FD1E29411D0020F89F /* Select Hashtags */ = { + isa = PBXGroup; + children = ( + 418830FE1E2941370020F89F /* SelectHashtagsViewController.swift */, + 41AA37F81E2D1C21005D5888 /* HashtagsDataSource.swift */, + ); + name = "Select Hashtags"; + sourceTree = ""; + }; + 41F223BB1E9979AF005BC4B6 /* Fonts */ = { + isa = PBXGroup; + children = ( + 41F223B51E9979A9005BC4B6 /* Montserrat-Bold.ttf */, + 41F223B61E9979A9005BC4B6 /* Montserrat-Regular.ttf */, + 41F223B71E9979A9005BC4B6 /* OFL.txt */, + ); + name = Fonts; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 4140EA3C1E27F8B800F448F7 /* TweetSample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4140EA4F1E27F8B900F448F7 /* Build configuration list for PBXNativeTarget "TweetSample" */; + buildPhases = ( + 4140EA391E27F8B800F448F7 /* Sources */, + 4140EA3A1E27F8B800F448F7 /* Frameworks */, + 4140EA3B1E27F8B800F448F7 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = TweetSample; + productName = TweetSample; + productReference = 4140EA3D1E27F8B900F448F7 /* TweetSample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 4140EA351E27F8B800F448F7 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0820; + LastUpgradeCheck = 0820; + ORGANIZATIONNAME = "Francois Courville"; + TargetAttributes = { + 4140EA3C1E27F8B800F448F7 = { + CreatedOnToolsVersion = 8.2.1; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 4140EA381E27F8B800F448F7 /* Build configuration list for PBXProject "TweetSample" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 4140EA341E27F8B800F448F7; + productRefGroup = 4140EA3E1E27F8B900F448F7 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 4140EA3C1E27F8B800F448F7 /* TweetSample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 4140EA3B1E27F8B800F448F7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 41F223BA1E9979A9005BC4B6 /* OFL.txt in Resources */, + 41F223B81E9979A9005BC4B6 /* Montserrat-Bold.ttf in Resources */, + 418830F41E2810390020F89F /* timeline.json in Resources */, + 4140EA4B1E27F8B900F448F7 /* LaunchScreen.storyboard in Resources */, + 41F223B91E9979A9005BC4B6 /* Montserrat-Regular.ttf in Resources */, + 4140EA481E27F8B900F448F7 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 4140EA391E27F8B800F448F7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 418831031E2943B20020F89F /* NewTweet.swift in Sources */, + 41F223B41E9978E2005BC4B6 /* Fonts.swift in Sources */, + 4140EA571E27FA0400F448F7 /* TweetListViewController.swift in Sources */, + 418831011E29424C0020F89F /* ComposeTextViewController.swift in Sources */, + 41F223B21E9976FA005BC4B6 /* Colors.swift in Sources */, + 418830FA1E293BB00020F89F /* TweetComposeCoordinator.swift in Sources */, + 4140EA5E1E27FF7A00F448F7 /* TweetCell.swift in Sources */, + 4140EA5A1E27FB2600F448F7 /* Tweet.swift in Sources */, + 4140EA5C1E27FDE900F448F7 /* TweetListDataSource.swift in Sources */, + 418831051E294C1C0020F89F /* SelectPhotoViewController.swift in Sources */, + 418830F81E292E880020F89F /* TweetDetailViewController.swift in Sources */, + 4140EA411E27F8B900F448F7 /* AppDelegate.swift in Sources */, + 41AA37F91E2D1C21005D5888 /* HashtagsDataSource.swift in Sources */, + 4140EA551E27F95700F448F7 /* TweetListCoordinator.swift in Sources */, + 418830FF1E2941370020F89F /* SelectHashtagsViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 4140EA491E27F8B900F448F7 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 4140EA4A1E27F8B900F448F7 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 4140EA4D1E27F8B900F448F7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.2; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 4140EA4E1E27F8B900F448F7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.2; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 4140EA501E27F8B900F448F7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = TweetSample/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.frankcourville.TweetSample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Debug; + }; + 4140EA511E27F8B900F448F7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = TweetSample/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.frankcourville.TweetSample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 4140EA381E27F8B800F448F7 /* Build configuration list for PBXProject "TweetSample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4140EA4D1E27F8B900F448F7 /* Debug */, + 4140EA4E1E27F8B900F448F7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4140EA4F1E27F8B900F448F7 /* Build configuration list for PBXNativeTarget "TweetSample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4140EA501E27F8B900F448F7 /* Debug */, + 4140EA511E27F8B900F448F7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 4140EA351E27F8B800F448F7 /* Project object */; +} diff --git a/TweetSample/AppDelegate.swift b/TweetSample/AppDelegate.swift new file mode 100644 index 0000000..4c6c023 --- /dev/null +++ b/TweetSample/AppDelegate.swift @@ -0,0 +1,39 @@ +// +// AppDelegate.swift +// TweetSample +// +// Created by Francois Courville on 2017-01-12. +// Copyright © 2017 Francois Courville. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + var rootCoordinator: TweetListCoordinator? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + + window = UIWindow() + rootCoordinator = TweetListCoordinator() + window?.rootViewController = rootCoordinator?.navigationController + window?.makeKeyAndVisible() + + UINavigationBarAppearance() + + return true + } + + func UINavigationBarAppearance() { + UINavigationBar.appearance().tintColor = .white + UINavigationBar.appearance().barTintColor = .appBlue + UINavigationBar.appearance().titleTextAttributes = [ NSFontAttributeName : UIFont.montserrat(size: 18), + NSForegroundColorAttributeName : UIColor.white ] + UIBarButtonItem.appearance(whenContainedInInstancesOf: [UINavigationBar.self]).setTitleTextAttributes([NSFontAttributeName : UIFont.montserrat(size: 14), + NSForegroundColorAttributeName : UIColor.white], + for: .normal) + } +} + diff --git a/TweetSample/Assets.xcassets/AppIcon.appiconset/Contents.json b/TweetSample/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..36d2c80 --- /dev/null +++ b/TweetSample/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/TweetSample/Base.lproj/LaunchScreen.storyboard b/TweetSample/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..fdf3f97 --- /dev/null +++ b/TweetSample/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TweetSample/Colors.swift b/TweetSample/Colors.swift new file mode 100644 index 0000000..9002000 --- /dev/null +++ b/TweetSample/Colors.swift @@ -0,0 +1,15 @@ +// +// Colors.swift +// TweetSample +// +// Created by Francois Courville on 2017-04-08. +// Copyright © 2017 Francois Courville. All rights reserved. +// + +import Foundation +import UIKit + +extension UIColor { + static let appBlue = UIColor(red:0.13, green:0.73, blue:0.84, alpha:1.0) + static let appCharcoal = UIColor(red:0.24, green:0.28, blue:0.31, alpha:1.0) +} diff --git a/TweetSample/ComposeTextViewController.swift b/TweetSample/ComposeTextViewController.swift new file mode 100644 index 0000000..17b61b0 --- /dev/null +++ b/TweetSample/ComposeTextViewController.swift @@ -0,0 +1,86 @@ +// +// ComposeTextViewController.swift +// TweetSample +// +// Created by Francois Courville on 2017-01-13. +// Copyright © 2017 Francois Courville. All rights reserved. +// + +import Foundation +import UIKit + +protocol ComposeTextViewControllerDelegate { + func didFinishComposing(text:String, in controller:ComposeTextViewController) +} + +class ComposeTextViewController: UIViewController { + var delegate: ComposeTextViewControllerDelegate? + + init() { + super.init(nibName: nil, bundle: nil) + } + + override func viewDidLoad() { + super.viewDidLoad() + + navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Next", style: .plain, target: self, action: #selector(handleNextButtonTapped)) + + setupView() + createConstraints() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + composeTextView.becomeFirstResponder() + } + + func handleNextButtonTapped() { + delegate?.didFinishComposing(text: composeTextView.text, in: self) + } + + func setupView() { + view.backgroundColor = UIColor.white + view.addSubview(titleLabel) + view.addSubview(composeTextView) + } + + func createConstraints() { + edgesForExtendedLayout = [] + + NSLayoutConstraint.activate([ + titleLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 12.0), + titleLabel.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor), + + composeTextView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 12), + composeTextView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor), + composeTextView.rightAnchor.constraint(equalTo: view.layoutMarginsGuide.rightAnchor), + composeTextView.heightAnchor.constraint(equalToConstant: 150) + ]) + } + + let composeTextView: UITextView = { + let textView = UITextView() + textView.font = UIFont.montserrat(size: 14.0) + textView.layer.cornerRadius = 5 + textView.layer.borderWidth = 1 + textView.layer.borderColor = UIColor.appCharcoal.cgColor + textView.translatesAutoresizingMaskIntoConstraints = false + + return textView + }() + + let titleLabel: UILabel = { + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + label.textColor = .appCharcoal + label.font = UIFont.montserratBold(size: 16.0) + label.text = "Tweet text:" + + return label + }() + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/TweetSample/Fonts.swift b/TweetSample/Fonts.swift new file mode 100644 index 0000000..63cab0a --- /dev/null +++ b/TweetSample/Fonts.swift @@ -0,0 +1,30 @@ +// +// Fonts.swift +// TweetSample +// +// Created by Francois Courville on 2017-04-08. +// Copyright © 2017 Francois Courville. All rights reserved. +// + +import Foundation +import UIKit + +extension UIFont { + static func montserrat(size: CGFloat) -> UIFont { + if let font = UIFont(name: "Montserrat-Regular", size: size) { + return font + } else { + print("Error loading montserrat") + return UIFont.systemFont(ofSize: size) + } + } + + static func montserratBold(size: CGFloat) -> UIFont { + if let font = UIFont(name: "Montserrat-Bold", size: size) { + return font + } else { + print("Error loading montserrat bold") + return UIFont.systemFont(ofSize: size) + } + } +} diff --git a/TweetSample/HashtagsDataSource.swift b/TweetSample/HashtagsDataSource.swift new file mode 100644 index 0000000..9ec4528 --- /dev/null +++ b/TweetSample/HashtagsDataSource.swift @@ -0,0 +1,33 @@ +// +// HashtagsDataSource.swift +// TweetSample +// +// Created by Francois Courville on 2017-01-16. +// Copyright © 2017 Francois Courville. All rights reserved. +// + +import Foundation +import UIKit + +class HashtagsDataSource : NSObject, UITableViewDataSource { + let hashtags: [String] = [ "#swift", "#iosDev", "#objc" ] + + func hashtag(at indexPath: IndexPath) -> String { + return hashtags[indexPath.row] + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return hashtags.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "hashtagCell", for: indexPath) + let hashtag = hashtags[indexPath.row] + + cell.textLabel?.text = hashtag + cell.textLabel?.font = UIFont.montserrat(size: 16) + cell.textLabel?.textColor = .appCharcoal + + return cell + } +} diff --git a/TweetSample/Info.plist b/TweetSample/Info.plist new file mode 100644 index 0000000..81a4851 --- /dev/null +++ b/TweetSample/Info.plist @@ -0,0 +1,54 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + NSPhotoLibraryUsageDescription + Need photos for tweets yo! + UIAppFonts + + Montserrat-Regular.ttf + Montserrat-Bold.ttf + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UIStatusBarStyle + UIStatusBarStyleLightContent + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/TweetSample/Montserrat-Bold.ttf b/TweetSample/Montserrat-Bold.ttf new file mode 100755 index 0000000000000000000000000000000000000000..ae33a4538132c8fc174dd53b3ce771009405d7a4 GIT binary patch literal 29560 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 literal 0 HcmV?d00001 diff --git a/TweetSample/Montserrat-Regular.ttf b/TweetSample/Montserrat-Regular.ttf new file mode 100755 index 0000000000000000000000000000000000000000..5b4b5afe6ee4b560b65b2f2040ad38f6c094b347 GIT binary patch literal 29016 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 literal 0 HcmV?d00001 diff --git a/TweetSample/NewTweet.swift b/TweetSample/NewTweet.swift new file mode 100644 index 0000000..0bfa1c9 --- /dev/null +++ b/TweetSample/NewTweet.swift @@ -0,0 +1,34 @@ +// +// NewTweet.swift +// TweetSample +// +// Created by Francois Courville on 2017-01-13. +// Copyright © 2017 Francois Courville. All rights reserved. +// + +import Foundation +import UIKit + +struct NewTweet : TweetDisplayable { + var text: String + var image: UIImage? + var hashtags: [String] + + init() { + text = "" + hashtags = [] + } + + func header() -> String { + return "Frank Courville \u{f8ff} - Frankacy" + } + + func content() -> String { + let hashtags = self.hashtags.joined(separator: " ") + return "\(text) - \(hashtags)" + } + + func tweetImage() -> UIImage? { + return image + } +} diff --git a/TweetSample/OFL.txt b/TweetSample/OFL.txt new file mode 100755 index 0000000..71caa53 --- /dev/null +++ b/TweetSample/OFL.txt @@ -0,0 +1,92 @@ +Copyright (c) 2011-2012, Julieta Ulanovsky (julieta.ulanovsky@gmail.com), with Reserved Font Names 'Montserrat' +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/TweetSample/SelectHashtagsViewController.swift b/TweetSample/SelectHashtagsViewController.swift new file mode 100644 index 0000000..71a512f --- /dev/null +++ b/TweetSample/SelectHashtagsViewController.swift @@ -0,0 +1,53 @@ +// +// SelectHashtagsViewController.swift +// TweetSample +// +// Created by Francois Courville on 2017-01-13. +// Copyright © 2017 Francois Courville. All rights reserved. +// + +import Foundation +import UIKit + +protocol SelectHashtagsViewControllerDelegate { + func didSelect(hashtags: [String], in controller: SelectHashtagsViewController) +} + +class SelectHashtagsViewController: UITableViewController { + let dataSource: HashtagsDataSource + var delegate: SelectHashtagsViewControllerDelegate? + + init(dataSource: HashtagsDataSource) { + self.dataSource = dataSource + + super.init(style: .plain) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + tableView.dataSource = self.dataSource + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "hashtagCell") + tableView.allowsMultipleSelection = true + tableView.tableFooterView = UIView() + tableView.reloadData() + + self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Next", style: .plain, target: self, action: #selector(handleNextButtonTapped)) + } + + func handleNextButtonTapped() { + var selectedHashtags: [String] = [] + + if let selectedIndexPaths = tableView.indexPathsForSelectedRows { + selectedHashtags = selectedIndexPaths.map { + return dataSource.hashtag(at: $0) + } + } + + delegate?.didSelect(hashtags: selectedHashtags, in: self) + } +} diff --git a/TweetSample/SelectPhotoViewController.swift b/TweetSample/SelectPhotoViewController.swift new file mode 100644 index 0000000..67ba665 --- /dev/null +++ b/TweetSample/SelectPhotoViewController.swift @@ -0,0 +1,105 @@ +// +// SelectPhotoViewController.swift +// TweetSample +// +// Created by Francois Courville on 2017-01-13. +// Copyright © 2017 Francois Courville. All rights reserved. +// + +import Foundation +import UIKit + +protocol SelectPhotoViewControllerDelegate { + func didSelect(photo: UIImage?, in controller: SelectPhotoViewController) +} + +class SelectPhotoViewController: UIViewController { + var delegate: SelectPhotoViewControllerDelegate? + var selectedPhoto: UIImage? { + didSet { + if let photo = selectedPhoto { + displaySelectedPhoto(photo: photo) + } + } + } + + override func viewDidLoad() { + super.viewDidLoad() + + navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Next", style: .plain, target: self, action: #selector(handleNextButtonTapped)) + + setupView() + createConstraints() + } + + func setupView() { + view.backgroundColor = .white + + view.addSubview(showImagePickerButton) + } + + func createConstraints() { + NSLayoutConstraint.activate([ + showImagePickerButton.centerXAnchor.constraint(equalTo: view.centerXAnchor), + showImagePickerButton.centerYAnchor.constraint(equalTo: view.centerYAnchor), + showImagePickerButton.heightAnchor.constraint(equalToConstant: 150), + showImagePickerButton.widthAnchor.constraint(equalToConstant: 150), + ]) + } + + func handleNextButtonTapped() { + delegate?.didSelect(photo: selectedPhoto, in: self) + } + + func handleShowImagePickerButtonPressed() { + guard UIImagePickerController.isSourceTypeAvailable(.photoLibrary) else { + return + } + + let pickerController = UIImagePickerController() + pickerController.delegate = self + present(pickerController, animated: true) + } + + func displaySelectedPhoto(photo: UIImage) { + showImagePickerButton.setTitle("", for: .normal) + showImagePickerButton.setBackgroundImage(photo, for: .normal) + } + + let showImagePickerButton: UIButton = { + let button = UIButton() + button.translatesAutoresizingMaskIntoConstraints = false + button.setTitle("Display\nImage Picker", for: .normal) + button.setTitleColor(.appCharcoal, for: .normal) + button.titleLabel?.font = UIFont.montserratBold(size: 14) + button.titleLabel?.numberOfLines = 2 + button.titleLabel?.textAlignment = .center + + button.layer.borderColor = UIColor.appCharcoal.cgColor + button.layer.borderWidth = 1 + button.layer.cornerRadius = 5 + button.layer.masksToBounds = true + + button.addTarget(self, action: #selector(handleShowImagePickerButtonPressed), for: .touchUpInside) + + return button + }() +} + +extension SelectPhotoViewController : UINavigationControllerDelegate {} +extension SelectPhotoViewController : UIImagePickerControllerDelegate { + func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { + dismiss(animated: true) + } + + func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { + guard let image = info["UIImagePickerControllerOriginalImage"] as? UIImage else { + dismiss(animated: true) + return + } + + selectedPhoto = image + dismiss(animated: true) + } +} + diff --git a/TweetSample/Tweet.swift b/TweetSample/Tweet.swift new file mode 100644 index 0000000..62409bb --- /dev/null +++ b/TweetSample/Tweet.swift @@ -0,0 +1,48 @@ +// +// Tweet.swift +// TweetSample +// +// Created by Francois Courville on 2017-01-12. +// Copyright © 2017 Francois Courville. All rights reserved. +// + +import Foundation +import UIKit + +protocol TweetDisplayable { + func header() -> String + func content() -> String + func tweetImage() -> UIImage? +} + +struct Tweet : TweetDisplayable { + let name: String + let handle: String + let text: String + + init?(json: [String: Any?]) { + guard let user = json["user"] as? [String: Any?], + let name = user["name"] as? String, + let handle = user["screen_name"] as? String, + let text = json["text"] as? String else { + return nil + } + + self.name = name + self.handle = handle + self.text = text + } + + func header() -> String { + return "\(name) - \(handle)" + } + + func content() -> String { + return text + } + + func tweetImage() -> UIImage? { + return nil + } + +} diff --git a/TweetSample/TweetCell.swift b/TweetSample/TweetCell.swift new file mode 100644 index 0000000..c839e0a --- /dev/null +++ b/TweetSample/TweetCell.swift @@ -0,0 +1,62 @@ +// +// TweetCell.swift +// TweetSample +// +// Created by Francois Courville on 2017-01-12. +// Copyright © 2017 Francois Courville. All rights reserved. +// + +import Foundation +import UIKit + +class TweetCell : UITableViewCell { + override init(style: UITableViewCellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + setupView() + setupConstraints() + } + + public func update(with tweet: Tweet) { + titleLabel.text = "\(tweet.name) - \(tweet.handle)" + contentLabel.text = tweet.text + } + + func setupView() { + contentView.backgroundColor = UIColor.white + contentView.addSubview(titleLabel) + contentView.addSubview(contentLabel) + } + + func setupConstraints() { + titleLabel.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor).isActive = true + titleLabel.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor).isActive = true + titleLabel.rightAnchor.constraint(equalTo: contentView.layoutMarginsGuide.rightAnchor).isActive = true + + contentLabel.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor).isActive = true + contentLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 10).isActive = true + contentLabel.rightAnchor.constraint(equalTo: contentView.layoutMarginsGuide.rightAnchor).isActive = true + contentLabel.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor).isActive = true + } + + let titleLabel: UILabel = { + let label = UILabel() + label.font = UIFont.montserratBold(size: 14.0) + label.translatesAutoresizingMaskIntoConstraints = false + label.textColor = .appCharcoal + return label + }() + + let contentLabel: UILabel = { + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + label.font = UIFont.montserrat(size: 12.0) + label.textColor = .appCharcoal + label.numberOfLines = 0 + return label + }() + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/TweetSample/TweetComposeCoordinator.swift b/TweetSample/TweetComposeCoordinator.swift new file mode 100644 index 0000000..e83fd16 --- /dev/null +++ b/TweetSample/TweetComposeCoordinator.swift @@ -0,0 +1,85 @@ +// +// TweetComposeCoordinator.swift +// TweetSample +// +// Created by Francois Courville on 2017-01-13. +// Copyright © 2017 Francois Courville. All rights reserved. +// + +import Foundation +import UIKit + +protocol TweetComposeCoordinatorDelegate { + func didFinishComposing(newTweet: NewTweet, in coordinator: TweetComposeCoordinator) +} + +class TweetComposeCoordinator { + var navigationController = UINavigationController() + + var newTweet: NewTweet = NewTweet() + var delegate: TweetComposeCoordinatorDelegate? + + init() { + navigationController.viewControllers = [ firstViewController() ] + } + + func firstViewController() -> UIViewController { + let composeController = ComposeTextViewController() + composeController.delegate = self + composeController.navigationItem.title = "Compose" + + return composeController + } + + fileprivate func pushSelectImageViewController() { + let photoController = SelectPhotoViewController() + photoController.delegate = self + photoController.navigationItem.title = "Photo" + + navigationController.pushViewController(photoController, animated: true) + } + + fileprivate func pushSelectHashtagsViewController() { + let hashtagsController = SelectHashtagsViewController(dataSource: HashtagsDataSource()) + hashtagsController.delegate = self + hashtagsController.navigationItem.title = "Hashags" + + navigationController.pushViewController(hashtagsController, animated: true) + } + + fileprivate func pushConfirmViewController() { + let confirmViewController = TweetDetailViewController(tweet: newTweet) + let confirmButton = UIBarButtonItem(title: "Confirm", style: .plain, target: self, action: #selector(completeTweetFlow)) + confirmViewController.navigationItem.rightBarButtonItem = confirmButton + confirmViewController.navigationItem.title = "Your new Tweet!" + + navigationController.pushViewController(confirmViewController, animated: true) + } + + @objc fileprivate func completeTweetFlow() { + delegate?.didFinishComposing(newTweet: newTweet, in: self) + } +} + +extension TweetComposeCoordinator: ComposeTextViewControllerDelegate { + func didFinishComposing(text: String, in controller: ComposeTextViewController) { + newTweet.text = text + pushSelectImageViewController() + } +} + +extension TweetComposeCoordinator: SelectPhotoViewControllerDelegate { + func didSelect(photo: UIImage?, in controller: SelectPhotoViewController) { + newTweet.image = photo; + pushSelectHashtagsViewController() + } +} + +extension TweetComposeCoordinator: SelectHashtagsViewControllerDelegate { + func didSelect(hashtags: [String], in controller: SelectHashtagsViewController) { + newTweet.hashtags = hashtags + pushConfirmViewController() +// completeTweetFlow() + } +} + diff --git a/TweetSample/TweetDetailViewController.swift b/TweetSample/TweetDetailViewController.swift new file mode 100644 index 0000000..24e56cd --- /dev/null +++ b/TweetSample/TweetDetailViewController.swift @@ -0,0 +1,99 @@ +// +// TweetDetailViewController.swift +// TweetSample +// +// Created by Francois Courville on 2017-01-13. +// Copyright © 2017 Francois Courville. All rights reserved. +// + +import Foundation +import UIKit + +class TweetDetailViewController: UIViewController { + let selectedTweet: TweetDisplayable + + let titleLabel: UILabel = { + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + label.font = UIFont.montserrat(size: 18.0) + label.textColor = .appCharcoal + + return label + }() + + let tweetImageView: UIImageView = { + let imageView = UIImageView() + imageView.translatesAutoresizingMaskIntoConstraints = false + + imageView.layer.borderColor = UIColor.appCharcoal.cgColor + imageView.layer.borderWidth = 1 + imageView.layer.cornerRadius = 5 + imageView.layer.masksToBounds = true + + return imageView + }() + + let contentLabel: UILabel = { + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + label.font = UIFont.montserrat(size: 14.0) + label.textColor = .appCharcoal + label.numberOfLines = 0 + + return label + }() + + init(tweet: TweetDisplayable) { + self.selectedTweet = tweet + + super.init(nibName: nil, bundle: nil) + } + + override func viewDidLoad() { + super.viewDidLoad() + + setupView() + createConstraints() + } + + func setupView() { + view.backgroundColor = UIColor.white + view.addSubview(titleLabel) + view.addSubview(contentLabel) + view.addSubview(tweetImageView) + + titleLabel.text = selectedTweet.header() + contentLabel.text = selectedTweet.content() + + if let image = selectedTweet.tweetImage() { + tweetImageView.isHidden = false + tweetImageView.image = image + } else { + tweetImageView.isHidden = true + tweetImageView.image = nil + } + } + + func createConstraints() { + edgesForExtendedLayout = [] + + NSLayoutConstraint.activate([ + titleLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 10), + titleLabel.leftAnchor.constraint(equalTo: view.layoutMarginsGuide.leftAnchor), + titleLabel.rightAnchor.constraint(equalTo: view.layoutMarginsGuide.rightAnchor), + + contentLabel.leftAnchor.constraint(equalTo: view.layoutMarginsGuide.leftAnchor), + contentLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 10), + contentLabel.rightAnchor.constraint(equalTo: view.layoutMarginsGuide.rightAnchor), + + tweetImageView.heightAnchor.constraint(equalToConstant: 150), + tweetImageView.widthAnchor.constraint(equalToConstant: 150), + tweetImageView.topAnchor.constraint(equalTo: contentLabel.bottomAnchor, constant: 10), + tweetImageView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor) + ]) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/TweetSample/TweetListCoordinator.swift b/TweetSample/TweetListCoordinator.swift new file mode 100644 index 0000000..7432ff4 --- /dev/null +++ b/TweetSample/TweetListCoordinator.swift @@ -0,0 +1,52 @@ +// +// TweetListCoordinator.swift +// TweetSample +// +// Created by Francois Courville on 2017-01-12. +// Copyright © 2017 Francois Courville. All rights reserved. +// + +import Foundation +import UIKit + +class TweetListCoordinator { + public var navigationController = UINavigationController() + + init() { + navigationController.viewControllers = [ firstViewController() ] + } + + func firstViewController() -> UIViewController { + let dataSource = TweetListDataSource() + let tweetList = TweetListViewController(dataSource: dataSource) + tweetList.navigationItem.title = "Tweets" + tweetList.delegate = self + tweetList.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "New", style: .plain, target: self, action: #selector(handleNewTweetButtonTapped)) + + return tweetList + } + + func pushTweetDetail(tweet: Tweet) { + let viewController = TweetDetailViewController(tweet: tweet) + navigationController.pushViewController(viewController, animated: true) + } + + @objc func handleNewTweetButtonTapped() { + let composeTweetCoordinator = TweetComposeCoordinator() + composeTweetCoordinator.delegate = self + navigationController.present(composeTweetCoordinator.navigationController, animated: true, completion: nil) + } +} + +extension TweetListCoordinator: TweetListViewControllerDelegate { + func didSelect(tweet: Tweet, in controller: TweetListViewController) { + pushTweetDetail(tweet: tweet) + } +} + +extension TweetListCoordinator: TweetComposeCoordinatorDelegate { + func didFinishComposing(newTweet: NewTweet, in coordinator: TweetComposeCoordinator) { + coordinator.navigationController.dismiss(animated: true, completion: nil) + print("This would be a good place to queue up a request for our API: new Tweet with text \"\(newTweet.text)\" and hashtags \(newTweet.hashtags)") + } +} diff --git a/TweetSample/TweetListDataSource.swift b/TweetSample/TweetListDataSource.swift new file mode 100644 index 0000000..9e897f5 --- /dev/null +++ b/TweetSample/TweetListDataSource.swift @@ -0,0 +1,43 @@ +// +// TweetListDataSource.swift +// TweetSample +// +// Created by Francois Courville on 2017-01-12. +// Copyright © 2017 Francois Courville. All rights reserved. +// + +import Foundation +import UIKit + +class TweetListDataSource : NSObject, UITableViewDataSource { + var tweets: [Tweet] = [] + + func loadTweets() { + guard let path = Bundle.main.url(forResource: "timeline", withExtension: "json") else { + return + } + + do { + let jsonData = try Data(contentsOf: path) + let entries = try JSONSerialization.jsonObject(with: jsonData, options: []) + let tweetJson = entries as! [[String: Any]] + tweets = tweetJson.flatMap({ + return Tweet(json: $0) + }) + } catch { + return + } + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return tweets.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "tweetCell", for: indexPath) as? TweetCell + let tweet = tweets[indexPath.row] + cell?.update(with: tweet) + + return cell! + } +} diff --git a/TweetSample/TweetListViewController.swift b/TweetSample/TweetListViewController.swift new file mode 100644 index 0000000..1a98682 --- /dev/null +++ b/TweetSample/TweetListViewController.swift @@ -0,0 +1,45 @@ +// +// TweetListViewController.swift +// TweetSample +// +// Created by Francois Courville on 2017-01-12. +// Copyright © 2017 Francois Courville. All rights reserved. +// + +import Foundation +import UIKit + +protocol TweetListViewControllerDelegate { + func didSelect(tweet: Tweet, in controller: TweetListViewController) +} + +class TweetListViewController : UITableViewController { + var delegate: TweetListViewControllerDelegate? + let dataSource: TweetListDataSource + + init(dataSource: TweetListDataSource) { + self.dataSource = dataSource + + super.init(style: .plain) + } + + override func viewDidLoad() { + super.viewDidLoad() + + dataSource.loadTweets() + + tableView.dataSource = self.dataSource + tableView.register(TweetCell.self, forCellReuseIdentifier: "tweetCell") + tableView.estimatedRowHeight = 75 + tableView.reloadData() + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let selectedTweet = self.dataSource.tweets[indexPath.row] + delegate?.didSelect(tweet: selectedTweet, in: self) + } + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/TweetSample/timeline.json b/TweetSample/timeline.json new file mode 100644 index 0000000..101469a --- /dev/null +++ b/TweetSample/timeline.json @@ -0,0 +1,2218 @@ + [ + { + "created_at": "Thu Jan 12 01:55:00 +0000 2017", + "id": 8.1936200724585e+17, + "id_str": "819362007245852674", + "text": "@JStheoriginal One. Million. Times. This.", + "truncated": false, + "entities": { + "hashtags": [ + + ], + "symbols": [ + + ], + "user_mentions": [ + { + "screen_name": "JStheoriginal", + "name": "Justin Stanley \ud83d\udc68\u200d\ud83d\udcbb", + "id": 18286161, + "id_str": "18286161", + "indices": [ + 0, + 14 + ] + } + ], + "urls": [ + + ] + }, + "source": "Tweetbot for i\u039fS<\/a>", + "in_reply_to_status_id": 8.1935654627876e+17, + "in_reply_to_status_id_str": "819356546278756352", + "in_reply_to_user_id": 18286161, + "in_reply_to_user_id_str": "18286161", + "in_reply_to_screen_name": "JStheoriginal", + "user": { + "id": 37583735, + "id_str": "37583735", + "name": "Frank Courville \uf8ff", + "screen_name": "Frankacy", + "location": "Montreal", + "description": "iOS Consultant, Trainer and Toastmaster. I also co-organize @CocoaHeadsMtl. ENTJ. \u24cb", + "url": "https:\/\/t.co\/yF8FrHWoAI", + "entities": { + "url": { + "urls": [ + { + "url": "https:\/\/t.co\/yF8FrHWoAI", + "expanded_url": "http:\/\/frankcourville.com", + "display_url": "frankcourville.com", + "indices": [ + 0, + 23 + ] + } + ] + }, + "description": { + "urls": [ + + ] + } + }, + "protected": false, + "followers_count": 334, + "friends_count": 360, + "listed_count": 20, + "created_at": "Mon May 04 03:25:47 +0000 2009", + "favourites_count": 1764, + "utc_offset": -18000, + "time_zone": "Eastern Time (US & Canada)", + "geo_enabled": false, + "verified": false, + "statuses_count": 2774, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "is_translation_enabled": false, + "profile_background_color": "C0DEED", + "profile_background_image_url": "http:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_image_url_https": "https:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_tile": true, + "profile_image_url": "http:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_image_url_https": "https:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_banner_url": "https:\/\/pbs.twimg.com\/profile_banners\/37583735\/1441593015", + "profile_link_color": "0000CC", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "has_extended_profile": false, + "default_profile": false, + "default_profile_image": false, + "following": false, + "follow_request_sent": false, + "notifications": false, + "translator_type": "none" + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "retweet_count": 0, + "favorite_count": 0, + "favorited": false, + "retweeted": false, + "lang": "en" + }, + { + "created_at": "Wed Jan 11 19:07:17 +0000 2017", + "id": 8.192594034719e+17, + "id_str": "819259403471900677", + "text": "@nsphin Any kernel panics or sleep-wake failures yet? I seem to get those all the time now \ud83d\ude36", + "truncated": false, + "entities": { + "hashtags": [ + + ], + "symbols": [ + + ], + "user_mentions": [ + { + "screen_name": "nsphin", + "name": "S\u00e9raphin Hochart", + "id": 79655770, + "id_str": "79655770", + "indices": [ + 0, + 7 + ] + } + ], + "urls": [ + + ] + }, + "source": "Tweetbot for Mac<\/a>", + "in_reply_to_status_id": 8.1925841206838e+17, + "in_reply_to_status_id_str": "819258412068376577", + "in_reply_to_user_id": 79655770, + "in_reply_to_user_id_str": "79655770", + "in_reply_to_screen_name": "nsphin", + "user": { + "id": 37583735, + "id_str": "37583735", + "name": "Frank Courville \uf8ff", + "screen_name": "Frankacy", + "location": "Montreal", + "description": "iOS Consultant, Trainer and Toastmaster. I also co-organize @CocoaHeadsMtl. ENTJ. \u24cb", + "url": "https:\/\/t.co\/yF8FrHWoAI", + "entities": { + "url": { + "urls": [ + { + "url": "https:\/\/t.co\/yF8FrHWoAI", + "expanded_url": "http:\/\/frankcourville.com", + "display_url": "frankcourville.com", + "indices": [ + 0, + 23 + ] + } + ] + }, + "description": { + "urls": [ + + ] + } + }, + "protected": false, + "followers_count": 334, + "friends_count": 360, + "listed_count": 20, + "created_at": "Mon May 04 03:25:47 +0000 2009", + "favourites_count": 1764, + "utc_offset": -18000, + "time_zone": "Eastern Time (US & Canada)", + "geo_enabled": false, + "verified": false, + "statuses_count": 2774, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "is_translation_enabled": false, + "profile_background_color": "C0DEED", + "profile_background_image_url": "http:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_image_url_https": "https:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_tile": true, + "profile_image_url": "http:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_image_url_https": "https:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_banner_url": "https:\/\/pbs.twimg.com\/profile_banners\/37583735\/1441593015", + "profile_link_color": "0000CC", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "has_extended_profile": false, + "default_profile": false, + "default_profile_image": false, + "following": false, + "follow_request_sent": false, + "notifications": false, + "translator_type": "none" + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "retweet_count": 0, + "favorite_count": 0, + "favorited": false, + "retweeted": false, + "lang": "en" + }, + { + "created_at": "Wed Jan 11 15:28:34 +0000 2017", + "id": 8.1920436081521e+17, + "id_str": "819204360815214592", + "text": "@merowing_ Great article! How do you feel about test feedback times with Xcode? I find they\u2019re dreadfully slow. Can we make it faster?", + "truncated": false, + "entities": { + "hashtags": [ + + ], + "symbols": [ + + ], + "user_mentions": [ + { + "screen_name": "merowing_", + "name": "Krzysztof Zab\u0142ocki", + "id": 289246577, + "id_str": "289246577", + "indices": [ + 0, + 10 + ] + } + ], + "urls": [ + + ] + }, + "source": "Tweetbot for Mac<\/a>", + "in_reply_to_status_id": 8.1888208413572e+17, + "in_reply_to_status_id_str": "818882084135718912", + "in_reply_to_user_id": 289246577, + "in_reply_to_user_id_str": "289246577", + "in_reply_to_screen_name": "merowing_", + "user": { + "id": 37583735, + "id_str": "37583735", + "name": "Frank Courville \uf8ff", + "screen_name": "Frankacy", + "location": "Montreal", + "description": "iOS Consultant, Trainer and Toastmaster. I also co-organize @CocoaHeadsMtl. ENTJ. \u24cb", + "url": "https:\/\/t.co\/yF8FrHWoAI", + "entities": { + "url": { + "urls": [ + { + "url": "https:\/\/t.co\/yF8FrHWoAI", + "expanded_url": "http:\/\/frankcourville.com", + "display_url": "frankcourville.com", + "indices": [ + 0, + 23 + ] + } + ] + }, + "description": { + "urls": [ + + ] + } + }, + "protected": false, + "followers_count": 334, + "friends_count": 360, + "listed_count": 20, + "created_at": "Mon May 04 03:25:47 +0000 2009", + "favourites_count": 1764, + "utc_offset": -18000, + "time_zone": "Eastern Time (US & Canada)", + "geo_enabled": false, + "verified": false, + "statuses_count": 2774, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "is_translation_enabled": false, + "profile_background_color": "C0DEED", + "profile_background_image_url": "http:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_image_url_https": "https:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_tile": true, + "profile_image_url": "http:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_image_url_https": "https:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_banner_url": "https:\/\/pbs.twimg.com\/profile_banners\/37583735\/1441593015", + "profile_link_color": "0000CC", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "has_extended_profile": false, + "default_profile": false, + "default_profile_image": false, + "following": false, + "follow_request_sent": false, + "notifications": false, + "translator_type": "none" + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "retweet_count": 0, + "favorite_count": 0, + "favorited": false, + "retweeted": false, + "lang": "en" + }, + { + "created_at": "Wed Jan 11 15:26:06 +0000 2017", + "id": 8.1920374155551e+17, + "id_str": "819203741555507202", + "text": "@nsphin Looking good \ud83d\ude0e", + "truncated": false, + "entities": { + "hashtags": [ + + ], + "symbols": [ + + ], + "user_mentions": [ + { + "screen_name": "nsphin", + "name": "S\u00e9raphin Hochart", + "id": 79655770, + "id_str": "79655770", + "indices": [ + 0, + 7 + ] + } + ], + "urls": [ + + ] + }, + "source": "Tweetbot for Mac<\/a>", + "in_reply_to_status_id": 8.1919762769059e+17, + "in_reply_to_status_id_str": "819197627690586112", + "in_reply_to_user_id": 79655770, + "in_reply_to_user_id_str": "79655770", + "in_reply_to_screen_name": "nsphin", + "user": { + "id": 37583735, + "id_str": "37583735", + "name": "Frank Courville \uf8ff", + "screen_name": "Frankacy", + "location": "Montreal", + "description": "iOS Consultant, Trainer and Toastmaster. I also co-organize @CocoaHeadsMtl. ENTJ. \u24cb", + "url": "https:\/\/t.co\/yF8FrHWoAI", + "entities": { + "url": { + "urls": [ + { + "url": "https:\/\/t.co\/yF8FrHWoAI", + "expanded_url": "http:\/\/frankcourville.com", + "display_url": "frankcourville.com", + "indices": [ + 0, + 23 + ] + } + ] + }, + "description": { + "urls": [ + + ] + } + }, + "protected": false, + "followers_count": 334, + "friends_count": 360, + "listed_count": 20, + "created_at": "Mon May 04 03:25:47 +0000 2009", + "favourites_count": 1764, + "utc_offset": -18000, + "time_zone": "Eastern Time (US & Canada)", + "geo_enabled": false, + "verified": false, + "statuses_count": 2774, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "is_translation_enabled": false, + "profile_background_color": "C0DEED", + "profile_background_image_url": "http:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_image_url_https": "https:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_tile": true, + "profile_image_url": "http:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_image_url_https": "https:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_banner_url": "https:\/\/pbs.twimg.com\/profile_banners\/37583735\/1441593015", + "profile_link_color": "0000CC", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "has_extended_profile": false, + "default_profile": false, + "default_profile_image": false, + "following": false, + "follow_request_sent": false, + "notifications": false, + "translator_type": "none" + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "retweet_count": 0, + "favorite_count": 0, + "favorited": false, + "retweeted": false, + "lang": "en" + }, + { + "created_at": "Wed Jan 11 13:20:15 +0000 2017", + "id": 8.1917206864214e+17, + "id_str": "819172068642136064", + "text": "@merowing_ wholeheartedly agree. I wish someone had forced me to compose more when I started out. Would\u2019ve avoided many headaches", + "truncated": false, + "entities": { + "hashtags": [ + + ], + "symbols": [ + + ], + "user_mentions": [ + { + "screen_name": "merowing_", + "name": "Krzysztof Zab\u0142ocki", + "id": 289246577, + "id_str": "289246577", + "indices": [ + 0, + 10 + ] + } + ], + "urls": [ + + ] + }, + "source": "Tweetbot for i\u039fS<\/a>", + "in_reply_to_status_id": 8.1912029776907e+17, + "in_reply_to_status_id_str": "819120297769074688", + "in_reply_to_user_id": 289246577, + "in_reply_to_user_id_str": "289246577", + "in_reply_to_screen_name": "merowing_", + "user": { + "id": 37583735, + "id_str": "37583735", + "name": "Frank Courville \uf8ff", + "screen_name": "Frankacy", + "location": "Montreal", + "description": "iOS Consultant, Trainer and Toastmaster. I also co-organize @CocoaHeadsMtl. ENTJ. \u24cb", + "url": "https:\/\/t.co\/yF8FrHWoAI", + "entities": { + "url": { + "urls": [ + { + "url": "https:\/\/t.co\/yF8FrHWoAI", + "expanded_url": "http:\/\/frankcourville.com", + "display_url": "frankcourville.com", + "indices": [ + 0, + 23 + ] + } + ] + }, + "description": { + "urls": [ + + ] + } + }, + "protected": false, + "followers_count": 334, + "friends_count": 360, + "listed_count": 20, + "created_at": "Mon May 04 03:25:47 +0000 2009", + "favourites_count": 1764, + "utc_offset": -18000, + "time_zone": "Eastern Time (US & Canada)", + "geo_enabled": false, + "verified": false, + "statuses_count": 2774, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "is_translation_enabled": false, + "profile_background_color": "C0DEED", + "profile_background_image_url": "http:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_image_url_https": "https:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_tile": true, + "profile_image_url": "http:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_image_url_https": "https:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_banner_url": "https:\/\/pbs.twimg.com\/profile_banners\/37583735\/1441593015", + "profile_link_color": "0000CC", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "has_extended_profile": false, + "default_profile": false, + "default_profile_image": false, + "following": false, + "follow_request_sent": false, + "notifications": false, + "translator_type": "none" + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "retweet_count": 0, + "favorite_count": 2, + "favorited": false, + "retweeted": false, + "lang": "en" + }, + { + "created_at": "Fri Jan 06 01:08:29 +0000 2017", + "id": 8.1717597592238e+17, + "id_str": "817175975922384896", + "text": "@lucvandal I dunno, for me it just happened. If I remember correctly, you can set how often the screen savers cycle in the settings", + "truncated": false, + "entities": { + "hashtags": [ + + ], + "symbols": [ + + ], + "user_mentions": [ + { + "screen_name": "lucvandal", + "name": "Luc Vandal", + "id": 69159937, + "id_str": "69159937", + "indices": [ + 0, + 10 + ] + } + ], + "urls": [ + + ] + }, + "source": "Tweetbot for i\u039fS<\/a>", + "in_reply_to_status_id": 8.1716075788436e+17, + "in_reply_to_status_id_str": "817160757884358657", + "in_reply_to_user_id": 69159937, + "in_reply_to_user_id_str": "69159937", + "in_reply_to_screen_name": "lucvandal", + "user": { + "id": 37583735, + "id_str": "37583735", + "name": "Frank Courville \uf8ff", + "screen_name": "Frankacy", + "location": "Montreal", + "description": "iOS Consultant, Trainer and Toastmaster. I also co-organize @CocoaHeadsMtl. ENTJ. \u24cb", + "url": "https:\/\/t.co\/yF8FrHWoAI", + "entities": { + "url": { + "urls": [ + { + "url": "https:\/\/t.co\/yF8FrHWoAI", + "expanded_url": "http:\/\/frankcourville.com", + "display_url": "frankcourville.com", + "indices": [ + 0, + 23 + ] + } + ] + }, + "description": { + "urls": [ + + ] + } + }, + "protected": false, + "followers_count": 334, + "friends_count": 360, + "listed_count": 20, + "created_at": "Mon May 04 03:25:47 +0000 2009", + "favourites_count": 1764, + "utc_offset": -18000, + "time_zone": "Eastern Time (US & Canada)", + "geo_enabled": false, + "verified": false, + "statuses_count": 2774, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "is_translation_enabled": false, + "profile_background_color": "C0DEED", + "profile_background_image_url": "http:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_image_url_https": "https:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_tile": true, + "profile_image_url": "http:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_image_url_https": "https:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_banner_url": "https:\/\/pbs.twimg.com\/profile_banners\/37583735\/1441593015", + "profile_link_color": "0000CC", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "has_extended_profile": false, + "default_profile": false, + "default_profile_image": false, + "following": false, + "follow_request_sent": false, + "notifications": false, + "translator_type": "none" + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "retweet_count": 0, + "favorite_count": 0, + "favorited": false, + "retweeted": false, + "lang": "en" + }, + { + "created_at": "Fri Jan 06 00:29:40 +0000 2017", + "id": 8.1716620504799e+17, + "id_str": "817166205047992322", + "text": "@vernalkick @ShelleyTran Hah! Touch\u00e9 \ud83e\udd3a", + "truncated": false, + "entities": { + "hashtags": [ + + ], + "symbols": [ + + ], + "user_mentions": [ + { + "screen_name": "vernalkick", + "name": "Kevin Clark", + "id": 32622651, + "id_str": "32622651", + "indices": [ + 0, + 11 + ] + }, + { + "screen_name": "ShelleyTran", + "name": "Shelley Tran", + "id": 51783424, + "id_str": "51783424", + "indices": [ + 12, + 24 + ] + } + ], + "urls": [ + + ] + }, + "source": "Tweetbot for i\u039fS<\/a>", + "in_reply_to_status_id": 8.1716376572463e+17, + "in_reply_to_status_id_str": "817163765724626944", + "in_reply_to_user_id": 32622651, + "in_reply_to_user_id_str": "32622651", + "in_reply_to_screen_name": "vernalkick", + "user": { + "id": 37583735, + "id_str": "37583735", + "name": "Frank Courville \uf8ff", + "screen_name": "Frankacy", + "location": "Montreal", + "description": "iOS Consultant, Trainer and Toastmaster. I also co-organize @CocoaHeadsMtl. ENTJ. \u24cb", + "url": "https:\/\/t.co\/yF8FrHWoAI", + "entities": { + "url": { + "urls": [ + { + "url": "https:\/\/t.co\/yF8FrHWoAI", + "expanded_url": "http:\/\/frankcourville.com", + "display_url": "frankcourville.com", + "indices": [ + 0, + 23 + ] + } + ] + }, + "description": { + "urls": [ + + ] + } + }, + "protected": false, + "followers_count": 334, + "friends_count": 360, + "listed_count": 20, + "created_at": "Mon May 04 03:25:47 +0000 2009", + "favourites_count": 1764, + "utc_offset": -18000, + "time_zone": "Eastern Time (US & Canada)", + "geo_enabled": false, + "verified": false, + "statuses_count": 2774, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "is_translation_enabled": false, + "profile_background_color": "C0DEED", + "profile_background_image_url": "http:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_image_url_https": "https:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_tile": true, + "profile_image_url": "http:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_image_url_https": "https:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_banner_url": "https:\/\/pbs.twimg.com\/profile_banners\/37583735\/1441593015", + "profile_link_color": "0000CC", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "has_extended_profile": false, + "default_profile": false, + "default_profile_image": false, + "following": false, + "follow_request_sent": false, + "notifications": false, + "translator_type": "none" + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "retweet_count": 0, + "favorite_count": 0, + "favorited": false, + "retweeted": false, + "lang": "fr" + }, + { + "created_at": "Thu Jan 05 22:26:57 +0000 2017", + "id": 8.1713532529011e+17, + "id_str": "817135325290106880", + "text": "@KarlHarnois \ud83d\ude31\ud83d\ude31\ud83d\ude31", + "truncated": false, + "entities": { + "hashtags": [ + + ], + "symbols": [ + + ], + "user_mentions": [ + { + "screen_name": "KarlHarnois", + "name": "Karl Rivest Harnois", + "id": 3214779742, + "id_str": "3214779742", + "indices": [ + 0, + 12 + ] + } + ], + "urls": [ + + ] + }, + "source": "Tweetbot for i\u039fS<\/a>", + "in_reply_to_status_id": 8.1712878295603e+17, + "in_reply_to_status_id_str": "817128782956032001", + "in_reply_to_user_id": 3214779742, + "in_reply_to_user_id_str": "3214779742", + "in_reply_to_screen_name": "KarlHarnois", + "user": { + "id": 37583735, + "id_str": "37583735", + "name": "Frank Courville \uf8ff", + "screen_name": "Frankacy", + "location": "Montreal", + "description": "iOS Consultant, Trainer and Toastmaster. I also co-organize @CocoaHeadsMtl. ENTJ. \u24cb", + "url": "https:\/\/t.co\/yF8FrHWoAI", + "entities": { + "url": { + "urls": [ + { + "url": "https:\/\/t.co\/yF8FrHWoAI", + "expanded_url": "http:\/\/frankcourville.com", + "display_url": "frankcourville.com", + "indices": [ + 0, + 23 + ] + } + ] + }, + "description": { + "urls": [ + + ] + } + }, + "protected": false, + "followers_count": 334, + "friends_count": 360, + "listed_count": 20, + "created_at": "Mon May 04 03:25:47 +0000 2009", + "favourites_count": 1764, + "utc_offset": -18000, + "time_zone": "Eastern Time (US & Canada)", + "geo_enabled": false, + "verified": false, + "statuses_count": 2774, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "is_translation_enabled": false, + "profile_background_color": "C0DEED", + "profile_background_image_url": "http:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_image_url_https": "https:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_tile": true, + "profile_image_url": "http:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_image_url_https": "https:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_banner_url": "https:\/\/pbs.twimg.com\/profile_banners\/37583735\/1441593015", + "profile_link_color": "0000CC", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "has_extended_profile": false, + "default_profile": false, + "default_profile_image": false, + "following": false, + "follow_request_sent": false, + "notifications": false, + "translator_type": "none" + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "retweet_count": 0, + "favorite_count": 1, + "favorited": false, + "retweeted": false, + "lang": "und" + }, + { + "created_at": "Thu Jan 05 15:52:10 +0000 2017", + "id": 8.1703597308321e+17, + "id_str": "817035973083205633", + "text": "@nsphin Oh no! Which?", + "truncated": false, + "entities": { + "hashtags": [ + + ], + "symbols": [ + + ], + "user_mentions": [ + { + "screen_name": "nsphin", + "name": "S\u00e9raphin Hochart", + "id": 79655770, + "id_str": "79655770", + "indices": [ + 0, + 7 + ] + } + ], + "urls": [ + + ] + }, + "source": "Tweetbot for i\u039fS<\/a>", + "in_reply_to_status_id": 8.1703345856443e+17, + "in_reply_to_status_id_str": "817033458564431873", + "in_reply_to_user_id": 79655770, + "in_reply_to_user_id_str": "79655770", + "in_reply_to_screen_name": "nsphin", + "user": { + "id": 37583735, + "id_str": "37583735", + "name": "Frank Courville \uf8ff", + "screen_name": "Frankacy", + "location": "Montreal", + "description": "iOS Consultant, Trainer and Toastmaster. I also co-organize @CocoaHeadsMtl. ENTJ. \u24cb", + "url": "https:\/\/t.co\/yF8FrHWoAI", + "entities": { + "url": { + "urls": [ + { + "url": "https:\/\/t.co\/yF8FrHWoAI", + "expanded_url": "http:\/\/frankcourville.com", + "display_url": "frankcourville.com", + "indices": [ + 0, + 23 + ] + } + ] + }, + "description": { + "urls": [ + + ] + } + }, + "protected": false, + "followers_count": 334, + "friends_count": 360, + "listed_count": 20, + "created_at": "Mon May 04 03:25:47 +0000 2009", + "favourites_count": 1764, + "utc_offset": -18000, + "time_zone": "Eastern Time (US & Canada)", + "geo_enabled": false, + "verified": false, + "statuses_count": 2774, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "is_translation_enabled": false, + "profile_background_color": "C0DEED", + "profile_background_image_url": "http:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_image_url_https": "https:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_tile": true, + "profile_image_url": "http:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_image_url_https": "https:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_banner_url": "https:\/\/pbs.twimg.com\/profile_banners\/37583735\/1441593015", + "profile_link_color": "0000CC", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "has_extended_profile": false, + "default_profile": false, + "default_profile_image": false, + "following": false, + "follow_request_sent": false, + "notifications": false, + "translator_type": "none" + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "retweet_count": 0, + "favorite_count": 0, + "favorited": false, + "retweeted": false, + "lang": "en" + }, + { + "created_at": "Thu Jan 05 14:44:08 +0000 2017", + "id": 8.1701885023139e+17, + "id_str": "817018850231390211", + "text": "Watching a video about how to setup @YNAB and this person just created a category to save up for a shotgun \ud83d\ude10 #Murica", + "truncated": false, + "entities": { + "hashtags": [ + { + "text": "Murica", + "indices": [ + 109, + 116 + ] + } + ], + "symbols": [ + + ], + "user_mentions": [ + { + "screen_name": "ynab", + "name": "You Need A Budget", + "id": 15466379, + "id_str": "15466379", + "indices": [ + 36, + 41 + ] + } + ], + "urls": [ + + ] + }, + "source": "Tweetbot for i\u039fS<\/a>", + "in_reply_to_status_id": null, + "in_reply_to_status_id_str": null, + "in_reply_to_user_id": null, + "in_reply_to_user_id_str": null, + "in_reply_to_screen_name": null, + "user": { + "id": 37583735, + "id_str": "37583735", + "name": "Frank Courville \uf8ff", + "screen_name": "Frankacy", + "location": "Montreal", + "description": "iOS Consultant, Trainer and Toastmaster. I also co-organize @CocoaHeadsMtl. ENTJ. \u24cb", + "url": "https:\/\/t.co\/yF8FrHWoAI", + "entities": { + "url": { + "urls": [ + { + "url": "https:\/\/t.co\/yF8FrHWoAI", + "expanded_url": "http:\/\/frankcourville.com", + "display_url": "frankcourville.com", + "indices": [ + 0, + 23 + ] + } + ] + }, + "description": { + "urls": [ + + ] + } + }, + "protected": false, + "followers_count": 334, + "friends_count": 360, + "listed_count": 20, + "created_at": "Mon May 04 03:25:47 +0000 2009", + "favourites_count": 1764, + "utc_offset": -18000, + "time_zone": "Eastern Time (US & Canada)", + "geo_enabled": false, + "verified": false, + "statuses_count": 2774, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "is_translation_enabled": false, + "profile_background_color": "C0DEED", + "profile_background_image_url": "http:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_image_url_https": "https:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_tile": true, + "profile_image_url": "http:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_image_url_https": "https:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_banner_url": "https:\/\/pbs.twimg.com\/profile_banners\/37583735\/1441593015", + "profile_link_color": "0000CC", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "has_extended_profile": false, + "default_profile": false, + "default_profile_image": false, + "following": false, + "follow_request_sent": false, + "notifications": false, + "translator_type": "none" + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "retweet_count": 0, + "favorite_count": 1, + "favorited": false, + "retweeted": false, + "lang": "en" + }, + { + "created_at": "Thu Jan 05 01:16:54 +0000 2017", + "id": 8.1681570691439e+17, + "id_str": "816815706914385926", + "text": "@JStheoriginal 3D Touch is really great for editing text :) I\u2019m positive you\u2019ll like it once you get your hands on it!", + "truncated": false, + "entities": { + "hashtags": [ + + ], + "symbols": [ + + ], + "user_mentions": [ + { + "screen_name": "JStheoriginal", + "name": "Justin Stanley \ud83d\udc68\u200d\ud83d\udcbb", + "id": 18286161, + "id_str": "18286161", + "indices": [ + 0, + 14 + ] + } + ], + "urls": [ + + ] + }, + "source": "Tweetbot for i\u039fS<\/a>", + "in_reply_to_status_id": 8.168021177511e+17, + "in_reply_to_status_id_str": "816802117751099392", + "in_reply_to_user_id": 18286161, + "in_reply_to_user_id_str": "18286161", + "in_reply_to_screen_name": "JStheoriginal", + "user": { + "id": 37583735, + "id_str": "37583735", + "name": "Frank Courville \uf8ff", + "screen_name": "Frankacy", + "location": "Montreal", + "description": "iOS Consultant, Trainer and Toastmaster. I also co-organize @CocoaHeadsMtl. ENTJ. \u24cb", + "url": "https:\/\/t.co\/yF8FrHWoAI", + "entities": { + "url": { + "urls": [ + { + "url": "https:\/\/t.co\/yF8FrHWoAI", + "expanded_url": "http:\/\/frankcourville.com", + "display_url": "frankcourville.com", + "indices": [ + 0, + 23 + ] + } + ] + }, + "description": { + "urls": [ + + ] + } + }, + "protected": false, + "followers_count": 334, + "friends_count": 360, + "listed_count": 20, + "created_at": "Mon May 04 03:25:47 +0000 2009", + "favourites_count": 1764, + "utc_offset": -18000, + "time_zone": "Eastern Time (US & Canada)", + "geo_enabled": false, + "verified": false, + "statuses_count": 2774, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "is_translation_enabled": false, + "profile_background_color": "C0DEED", + "profile_background_image_url": "http:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_image_url_https": "https:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_tile": true, + "profile_image_url": "http:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_image_url_https": "https:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_banner_url": "https:\/\/pbs.twimg.com\/profile_banners\/37583735\/1441593015", + "profile_link_color": "0000CC", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "has_extended_profile": false, + "default_profile": false, + "default_profile_image": false, + "following": false, + "follow_request_sent": false, + "notifications": false, + "translator_type": "none" + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "retweet_count": 0, + "favorite_count": 1, + "favorited": false, + "retweeted": false, + "lang": "en" + }, + { + "created_at": "Wed Jan 04 18:10:00 +0000 2017", + "id": 8.1670827209492e+17, + "id_str": "816708272094924801", + "text": "@JStheoriginal what kind of bug are you? A \ud83d\udc1d? Or perhaps a \ud83d\udc1b?", + "truncated": false, + "entities": { + "hashtags": [ + + ], + "symbols": [ + + ], + "user_mentions": [ + { + "screen_name": "JStheoriginal", + "name": "Justin Stanley \ud83d\udc68\u200d\ud83d\udcbb", + "id": 18286161, + "id_str": "18286161", + "indices": [ + 0, + 14 + ] + } + ], + "urls": [ + + ] + }, + "source": "Tweetbot for i\u039fS<\/a>", + "in_reply_to_status_id": 8.1670754831741e+17, + "in_reply_to_status_id_str": "816707548317413376", + "in_reply_to_user_id": 18286161, + "in_reply_to_user_id_str": "18286161", + "in_reply_to_screen_name": "JStheoriginal", + "user": { + "id": 37583735, + "id_str": "37583735", + "name": "Frank Courville \uf8ff", + "screen_name": "Frankacy", + "location": "Montreal", + "description": "iOS Consultant, Trainer and Toastmaster. I also co-organize @CocoaHeadsMtl. ENTJ. \u24cb", + "url": "https:\/\/t.co\/yF8FrHWoAI", + "entities": { + "url": { + "urls": [ + { + "url": "https:\/\/t.co\/yF8FrHWoAI", + "expanded_url": "http:\/\/frankcourville.com", + "display_url": "frankcourville.com", + "indices": [ + 0, + 23 + ] + } + ] + }, + "description": { + "urls": [ + + ] + } + }, + "protected": false, + "followers_count": 334, + "friends_count": 360, + "listed_count": 20, + "created_at": "Mon May 04 03:25:47 +0000 2009", + "favourites_count": 1764, + "utc_offset": -18000, + "time_zone": "Eastern Time (US & Canada)", + "geo_enabled": false, + "verified": false, + "statuses_count": 2774, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "is_translation_enabled": false, + "profile_background_color": "C0DEED", + "profile_background_image_url": "http:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_image_url_https": "https:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_tile": true, + "profile_image_url": "http:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_image_url_https": "https:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_banner_url": "https:\/\/pbs.twimg.com\/profile_banners\/37583735\/1441593015", + "profile_link_color": "0000CC", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "has_extended_profile": false, + "default_profile": false, + "default_profile_image": false, + "following": false, + "follow_request_sent": false, + "notifications": false, + "translator_type": "none" + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "retweet_count": 0, + "favorite_count": 0, + "favorited": false, + "retweeted": false, + "lang": "en" + }, + { + "created_at": "Wed Jan 04 15:41:25 +0000 2017", + "id": 8.1667087969862e+17, + "id_str": "816670879698616320", + "text": "@AngeloStavrow I feel for you. When I learned my writer cousin did all her writing in Word, I was like, oh let me intro you to Ulysses \ud83d\ude07", + "truncated": false, + "entities": { + "hashtags": [ + + ], + "symbols": [ + + ], + "user_mentions": [ + { + "screen_name": "AngeloStavrow", + "name": "Angelo Stavrow", + "id": 15496351, + "id_str": "15496351", + "indices": [ + 0, + 14 + ] + } + ], + "urls": [ + + ] + }, + "source": "Tweetbot for i\u039fS<\/a>", + "in_reply_to_status_id": 8.1666571380084e+17, + "in_reply_to_status_id_str": "816665713800839168", + "in_reply_to_user_id": 15496351, + "in_reply_to_user_id_str": "15496351", + "in_reply_to_screen_name": "AngeloStavrow", + "user": { + "id": 37583735, + "id_str": "37583735", + "name": "Frank Courville \uf8ff", + "screen_name": "Frankacy", + "location": "Montreal", + "description": "iOS Consultant, Trainer and Toastmaster. I also co-organize @CocoaHeadsMtl. ENTJ. \u24cb", + "url": "https:\/\/t.co\/yF8FrHWoAI", + "entities": { + "url": { + "urls": [ + { + "url": "https:\/\/t.co\/yF8FrHWoAI", + "expanded_url": "http:\/\/frankcourville.com", + "display_url": "frankcourville.com", + "indices": [ + 0, + 23 + ] + } + ] + }, + "description": { + "urls": [ + + ] + } + }, + "protected": false, + "followers_count": 334, + "friends_count": 360, + "listed_count": 20, + "created_at": "Mon May 04 03:25:47 +0000 2009", + "favourites_count": 1764, + "utc_offset": -18000, + "time_zone": "Eastern Time (US & Canada)", + "geo_enabled": false, + "verified": false, + "statuses_count": 2774, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "is_translation_enabled": false, + "profile_background_color": "C0DEED", + "profile_background_image_url": "http:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_image_url_https": "https:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_tile": true, + "profile_image_url": "http:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_image_url_https": "https:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_banner_url": "https:\/\/pbs.twimg.com\/profile_banners\/37583735\/1441593015", + "profile_link_color": "0000CC", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "has_extended_profile": false, + "default_profile": false, + "default_profile_image": false, + "following": false, + "follow_request_sent": false, + "notifications": false, + "translator_type": "none" + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "retweet_count": 0, + "favorite_count": 0, + "favorited": false, + "retweeted": false, + "lang": "en" + }, + { + "created_at": "Wed Jan 04 14:10:06 +0000 2017", + "id": 8.1664790066305e+17, + "id_str": "816647900663050240", + "text": "@AngeloStavrow it depends on Jupiter\u2019s alignment. Duhhh!!!! \ud83d\ude07", + "truncated": false, + "entities": { + "hashtags": [ + + ], + "symbols": [ + + ], + "user_mentions": [ + { + "screen_name": "AngeloStavrow", + "name": "Angelo Stavrow", + "id": 15496351, + "id_str": "15496351", + "indices": [ + 0, + 14 + ] + } + ], + "urls": [ + + ] + }, + "source": "Tweetbot for i\u039fS<\/a>", + "in_reply_to_status_id": 8.1664169105046e+17, + "in_reply_to_status_id_str": "816641691050459136", + "in_reply_to_user_id": 15496351, + "in_reply_to_user_id_str": "15496351", + "in_reply_to_screen_name": "AngeloStavrow", + "user": { + "id": 37583735, + "id_str": "37583735", + "name": "Frank Courville \uf8ff", + "screen_name": "Frankacy", + "location": "Montreal", + "description": "iOS Consultant, Trainer and Toastmaster. I also co-organize @CocoaHeadsMtl. ENTJ. \u24cb", + "url": "https:\/\/t.co\/yF8FrHWoAI", + "entities": { + "url": { + "urls": [ + { + "url": "https:\/\/t.co\/yF8FrHWoAI", + "expanded_url": "http:\/\/frankcourville.com", + "display_url": "frankcourville.com", + "indices": [ + 0, + 23 + ] + } + ] + }, + "description": { + "urls": [ + + ] + } + }, + "protected": false, + "followers_count": 334, + "friends_count": 360, + "listed_count": 20, + "created_at": "Mon May 04 03:25:47 +0000 2009", + "favourites_count": 1764, + "utc_offset": -18000, + "time_zone": "Eastern Time (US & Canada)", + "geo_enabled": false, + "verified": false, + "statuses_count": 2774, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "is_translation_enabled": false, + "profile_background_color": "C0DEED", + "profile_background_image_url": "http:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_image_url_https": "https:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_tile": true, + "profile_image_url": "http:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_image_url_https": "https:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_banner_url": "https:\/\/pbs.twimg.com\/profile_banners\/37583735\/1441593015", + "profile_link_color": "0000CC", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "has_extended_profile": false, + "default_profile": false, + "default_profile_image": false, + "following": false, + "follow_request_sent": false, + "notifications": false, + "translator_type": "none" + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "retweet_count": 0, + "favorite_count": 0, + "favorited": false, + "retweeted": false, + "lang": "en" + }, + { + "created_at": "Wed Jan 04 00:26:45 +0000 2017", + "id": 8.1644069740656e+17, + "id_str": "816440697406558211", + "text": "@1Password \u201cno way\u201d as in the API does\u2019t exist? If so, is there a radar open?", + "truncated": false, + "entities": { + "hashtags": [ + + ], + "symbols": [ + + ], + "user_mentions": [ + { + "screen_name": "1Password", + "name": "1Password", + "id": 793926, + "id_str": "793926", + "indices": [ + 0, + 10 + ] + } + ], + "urls": [ + + ] + }, + "source": "Tweetbot for i\u039fS<\/a>", + "in_reply_to_status_id": 8.164393952974e+17, + "in_reply_to_status_id_str": "816439395297402880", + "in_reply_to_user_id": 793926, + "in_reply_to_user_id_str": "793926", + "in_reply_to_screen_name": "1Password", + "user": { + "id": 37583735, + "id_str": "37583735", + "name": "Frank Courville \uf8ff", + "screen_name": "Frankacy", + "location": "Montreal", + "description": "iOS Consultant, Trainer and Toastmaster. I also co-organize @CocoaHeadsMtl. ENTJ. \u24cb", + "url": "https:\/\/t.co\/yF8FrHWoAI", + "entities": { + "url": { + "urls": [ + { + "url": "https:\/\/t.co\/yF8FrHWoAI", + "expanded_url": "http:\/\/frankcourville.com", + "display_url": "frankcourville.com", + "indices": [ + 0, + 23 + ] + } + ] + }, + "description": { + "urls": [ + + ] + } + }, + "protected": false, + "followers_count": 334, + "friends_count": 360, + "listed_count": 20, + "created_at": "Mon May 04 03:25:47 +0000 2009", + "favourites_count": 1764, + "utc_offset": -18000, + "time_zone": "Eastern Time (US & Canada)", + "geo_enabled": false, + "verified": false, + "statuses_count": 2774, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "is_translation_enabled": false, + "profile_background_color": "C0DEED", + "profile_background_image_url": "http:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_image_url_https": "https:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_tile": true, + "profile_image_url": "http:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_image_url_https": "https:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_banner_url": "https:\/\/pbs.twimg.com\/profile_banners\/37583735\/1441593015", + "profile_link_color": "0000CC", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "has_extended_profile": false, + "default_profile": false, + "default_profile_image": false, + "following": false, + "follow_request_sent": false, + "notifications": false, + "translator_type": "none" + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "retweet_count": 0, + "favorite_count": 0, + "favorited": false, + "retweeted": false, + "lang": "en" + }, + { + "created_at": "Wed Jan 04 00:04:52 +0000 2017", + "id": 8.164351896227e+17, + "id_str": "816435189622702081", + "text": "@smileyborg hah, same here! (and I still pronounce it re-flog \ud83d\ude4a)", + "truncated": false, + "entities": { + "hashtags": [ + + ], + "symbols": [ + + ], + "user_mentions": [ + { + "screen_name": "smileyborg", + "name": "Tyler Fox", + "id": 1636577430, + "id_str": "1636577430", + "indices": [ + 0, + 11 + ] + } + ], + "urls": [ + + ] + }, + "source": "Tweetbot for i\u039fS<\/a>", + "in_reply_to_status_id": 8.1643208849675e+17, + "in_reply_to_status_id_str": "816432088496754688", + "in_reply_to_user_id": 1636577430, + "in_reply_to_user_id_str": "1636577430", + "in_reply_to_screen_name": "smileyborg", + "user": { + "id": 37583735, + "id_str": "37583735", + "name": "Frank Courville \uf8ff", + "screen_name": "Frankacy", + "location": "Montreal", + "description": "iOS Consultant, Trainer and Toastmaster. I also co-organize @CocoaHeadsMtl. ENTJ. \u24cb", + "url": "https:\/\/t.co\/yF8FrHWoAI", + "entities": { + "url": { + "urls": [ + { + "url": "https:\/\/t.co\/yF8FrHWoAI", + "expanded_url": "http:\/\/frankcourville.com", + "display_url": "frankcourville.com", + "indices": [ + 0, + 23 + ] + } + ] + }, + "description": { + "urls": [ + + ] + } + }, + "protected": false, + "followers_count": 334, + "friends_count": 360, + "listed_count": 20, + "created_at": "Mon May 04 03:25:47 +0000 2009", + "favourites_count": 1764, + "utc_offset": -18000, + "time_zone": "Eastern Time (US & Canada)", + "geo_enabled": false, + "verified": false, + "statuses_count": 2774, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "is_translation_enabled": false, + "profile_background_color": "C0DEED", + "profile_background_image_url": "http:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_image_url_https": "https:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_tile": true, + "profile_image_url": "http:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_image_url_https": "https:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_banner_url": "https:\/\/pbs.twimg.com\/profile_banners\/37583735\/1441593015", + "profile_link_color": "0000CC", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "has_extended_profile": false, + "default_profile": false, + "default_profile_image": false, + "following": false, + "follow_request_sent": false, + "notifications": false, + "translator_type": "none" + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "retweet_count": 0, + "favorite_count": 2, + "favorited": false, + "retweeted": false, + "lang": "en" + }, + { + "created_at": "Tue Jan 03 22:51:05 +0000 2017", + "id": 8.1641661965214e+17, + "id_str": "816416619652141056", + "text": "@kcase Especially with emoji! It would be great if OF could autocomplete the folder part and let me create a new project with CMD+Enter", + "truncated": false, + "entities": { + "hashtags": [ + + ], + "symbols": [ + + ], + "user_mentions": [ + { + "screen_name": "kcase", + "name": "Ken Case", + "id": 7548842, + "id_str": "7548842", + "indices": [ + 0, + 6 + ] + } + ], + "urls": [ + + ] + }, + "source": "Tweetbot for Mac<\/a>", + "in_reply_to_status_id": 8.164160377973e+17, + "in_reply_to_status_id_str": "816416037797302272", + "in_reply_to_user_id": 37583735, + "in_reply_to_user_id_str": "37583735", + "in_reply_to_screen_name": "Frankacy", + "user": { + "id": 37583735, + "id_str": "37583735", + "name": "Frank Courville \uf8ff", + "screen_name": "Frankacy", + "location": "Montreal", + "description": "iOS Consultant, Trainer and Toastmaster. I also co-organize @CocoaHeadsMtl. ENTJ. \u24cb", + "url": "https:\/\/t.co\/yF8FrHWoAI", + "entities": { + "url": { + "urls": [ + { + "url": "https:\/\/t.co\/yF8FrHWoAI", + "expanded_url": "http:\/\/frankcourville.com", + "display_url": "frankcourville.com", + "indices": [ + 0, + 23 + ] + } + ] + }, + "description": { + "urls": [ + + ] + } + }, + "protected": false, + "followers_count": 334, + "friends_count": 360, + "listed_count": 20, + "created_at": "Mon May 04 03:25:47 +0000 2009", + "favourites_count": 1764, + "utc_offset": -18000, + "time_zone": "Eastern Time (US & Canada)", + "geo_enabled": false, + "verified": false, + "statuses_count": 2774, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "is_translation_enabled": false, + "profile_background_color": "C0DEED", + "profile_background_image_url": "http:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_image_url_https": "https:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_tile": true, + "profile_image_url": "http:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_image_url_https": "https:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_banner_url": "https:\/\/pbs.twimg.com\/profile_banners\/37583735\/1441593015", + "profile_link_color": "0000CC", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "has_extended_profile": false, + "default_profile": false, + "default_profile_image": false, + "following": false, + "follow_request_sent": false, + "notifications": false, + "translator_type": "none" + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "retweet_count": 0, + "favorite_count": 0, + "favorited": false, + "retweeted": false, + "lang": "en" + }, + { + "created_at": "Tue Jan 03 22:48:46 +0000 2017", + "id": 8.164160377973e+17, + "id_str": "816416037797302272", + "text": "@kcase I create new projects from inbox items. Needing to type out exact folder names to have the new project nested correctly is tough :(", + "truncated": false, + "entities": { + "hashtags": [ + + ], + "symbols": [ + + ], + "user_mentions": [ + { + "screen_name": "kcase", + "name": "Ken Case", + "id": 7548842, + "id_str": "7548842", + "indices": [ + 0, + 6 + ] + } + ], + "urls": [ + + ] + }, + "source": "Tweetbot for Mac<\/a>", + "in_reply_to_status_id": 8.16414717447e+17, + "in_reply_to_status_id_str": "816414717446995968", + "in_reply_to_user_id": 7548842, + "in_reply_to_user_id_str": "7548842", + "in_reply_to_screen_name": "kcase", + "user": { + "id": 37583735, + "id_str": "37583735", + "name": "Frank Courville \uf8ff", + "screen_name": "Frankacy", + "location": "Montreal", + "description": "iOS Consultant, Trainer and Toastmaster. I also co-organize @CocoaHeadsMtl. ENTJ. \u24cb", + "url": "https:\/\/t.co\/yF8FrHWoAI", + "entities": { + "url": { + "urls": [ + { + "url": "https:\/\/t.co\/yF8FrHWoAI", + "expanded_url": "http:\/\/frankcourville.com", + "display_url": "frankcourville.com", + "indices": [ + 0, + 23 + ] + } + ] + }, + "description": { + "urls": [ + + ] + } + }, + "protected": false, + "followers_count": 334, + "friends_count": 360, + "listed_count": 20, + "created_at": "Mon May 04 03:25:47 +0000 2009", + "favourites_count": 1764, + "utc_offset": -18000, + "time_zone": "Eastern Time (US & Canada)", + "geo_enabled": false, + "verified": false, + "statuses_count": 2774, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "is_translation_enabled": false, + "profile_background_color": "C0DEED", + "profile_background_image_url": "http:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_image_url_https": "https:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_tile": true, + "profile_image_url": "http:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_image_url_https": "https:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_banner_url": "https:\/\/pbs.twimg.com\/profile_banners\/37583735\/1441593015", + "profile_link_color": "0000CC", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "has_extended_profile": false, + "default_profile": false, + "default_profile_image": false, + "following": false, + "follow_request_sent": false, + "notifications": false, + "translator_type": "none" + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "retweet_count": 0, + "favorite_count": 0, + "favorited": false, + "retweeted": false, + "lang": "en" + }, + { + "created_at": "Tue Jan 03 22:38:28 +0000 2017", + "id": 8.1641344788172e+17, + "id_str": "816413447881719808", + "text": "@steipete @olebegemann Though I have no way of knowing if it\u2019s \u201cthe best\u201d way or not. \ud83e\udd37\ud83c\udffb\u200d\u2642\ufe0f", + "truncated": false, + "entities": { + "hashtags": [ + + ], + "symbols": [ + + ], + "user_mentions": [ + { + "screen_name": "steipete", + "name": "Peter Steinberger", + "id": 25401953, + "id_str": "25401953", + "indices": [ + 0, + 9 + ] + }, + { + "screen_name": "olebegemann", + "name": "Ole Begemann", + "id": 231756759, + "id_str": "231756759", + "indices": [ + 10, + 22 + ] + } + ], + "urls": [ + + ] + }, + "source": "Tweetbot for Mac<\/a>", + "in_reply_to_status_id": 8.164131634911e+17, + "in_reply_to_status_id_str": "816413163491098626", + "in_reply_to_user_id": 37583735, + "in_reply_to_user_id_str": "37583735", + "in_reply_to_screen_name": "Frankacy", + "user": { + "id": 37583735, + "id_str": "37583735", + "name": "Frank Courville \uf8ff", + "screen_name": "Frankacy", + "location": "Montreal", + "description": "iOS Consultant, Trainer and Toastmaster. I also co-organize @CocoaHeadsMtl. ENTJ. \u24cb", + "url": "https:\/\/t.co\/yF8FrHWoAI", + "entities": { + "url": { + "urls": [ + { + "url": "https:\/\/t.co\/yF8FrHWoAI", + "expanded_url": "http:\/\/frankcourville.com", + "display_url": "frankcourville.com", + "indices": [ + 0, + 23 + ] + } + ] + }, + "description": { + "urls": [ + + ] + } + }, + "protected": false, + "followers_count": 334, + "friends_count": 360, + "listed_count": 20, + "created_at": "Mon May 04 03:25:47 +0000 2009", + "favourites_count": 1764, + "utc_offset": -18000, + "time_zone": "Eastern Time (US & Canada)", + "geo_enabled": false, + "verified": false, + "statuses_count": 2774, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "is_translation_enabled": false, + "profile_background_color": "C0DEED", + "profile_background_image_url": "http:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_image_url_https": "https:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_tile": true, + "profile_image_url": "http:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_image_url_https": "https:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_banner_url": "https:\/\/pbs.twimg.com\/profile_banners\/37583735\/1441593015", + "profile_link_color": "0000CC", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "has_extended_profile": false, + "default_profile": false, + "default_profile_image": false, + "following": false, + "follow_request_sent": false, + "notifications": false, + "translator_type": "none" + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "retweet_count": 0, + "favorite_count": 0, + "favorited": false, + "retweeted": false, + "lang": "en" + }, + { + "created_at": "Tue Jan 03 22:37:21 +0000 2017", + "id": 8.164131634911e+17, + "id_str": "816413163491098626", + "text": "@steipete @olebegemann If I know a view\u2019s constraints won\u2019t be changing, I create them in the init. Hasn\u2019t failed me so far :)", + "truncated": false, + "entities": { + "hashtags": [ + + ], + "symbols": [ + + ], + "user_mentions": [ + { + "screen_name": "steipete", + "name": "Peter Steinberger", + "id": 25401953, + "id_str": "25401953", + "indices": [ + 0, + 9 + ] + }, + { + "screen_name": "olebegemann", + "name": "Ole Begemann", + "id": 231756759, + "id_str": "231756759", + "indices": [ + 10, + 22 + ] + } + ], + "urls": [ + + ] + }, + "source": "Tweetbot for Mac<\/a>", + "in_reply_to_status_id": 8.1641268886365e+17, + "in_reply_to_status_id_str": "816412688863653889", + "in_reply_to_user_id": 25401953, + "in_reply_to_user_id_str": "25401953", + "in_reply_to_screen_name": "steipete", + "user": { + "id": 37583735, + "id_str": "37583735", + "name": "Frank Courville \uf8ff", + "screen_name": "Frankacy", + "location": "Montreal", + "description": "iOS Consultant, Trainer and Toastmaster. I also co-organize @CocoaHeadsMtl. ENTJ. \u24cb", + "url": "https:\/\/t.co\/yF8FrHWoAI", + "entities": { + "url": { + "urls": [ + { + "url": "https:\/\/t.co\/yF8FrHWoAI", + "expanded_url": "http:\/\/frankcourville.com", + "display_url": "frankcourville.com", + "indices": [ + 0, + 23 + ] + } + ] + }, + "description": { + "urls": [ + + ] + } + }, + "protected": false, + "followers_count": 334, + "friends_count": 360, + "listed_count": 20, + "created_at": "Mon May 04 03:25:47 +0000 2009", + "favourites_count": 1764, + "utc_offset": -18000, + "time_zone": "Eastern Time (US & Canada)", + "geo_enabled": false, + "verified": false, + "statuses_count": 2774, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "is_translation_enabled": false, + "profile_background_color": "C0DEED", + "profile_background_image_url": "http:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_image_url_https": "https:\/\/pbs.twimg.com\/profile_background_images\/786695929\/f4ad1fd259c8c7d81c8e864cd0248213.png", + "profile_background_tile": true, + "profile_image_url": "http:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_image_url_https": "https:\/\/pbs.twimg.com\/profile_images\/640708391153741824\/2rmo7Fvl_normal.jpg", + "profile_banner_url": "https:\/\/pbs.twimg.com\/profile_banners\/37583735\/1441593015", + "profile_link_color": "0000CC", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "has_extended_profile": false, + "default_profile": false, + "default_profile_image": false, + "following": false, + "follow_request_sent": false, + "notifications": false, + "translator_type": "none" + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "retweet_count": 0, + "favorite_count": 4, + "favorited": false, + "retweeted": false, + "lang": "en" + } + ]