├── .gitattributes ├── .gitignore ├── .travis.yml ├── Cartfile.resolved ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── Podfile ├── Podfile.lock ├── README.md ├── Swift-ZHI.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── Swift-ZHI.xcscheme ├── Swift-ZHI.xcworkspace └── contents.xcworkspacedata ├── SwiftDaily-ZhiHu ├── .gitkeep ├── Controllers │ ├── .gitkeep │ ├── AppDelegate.swift │ ├── DailyTableViewController.swift │ ├── HideNavBarViewController.swift │ ├── HidesHairLineUnderNavBarViewController.swift │ ├── NewsViewController.swift │ ├── RealmCommentViewController.swift │ ├── RealmDailyTableViewController.swift │ └── RealmNewsViewController.swift ├── Extensions │ └── .gitkeep ├── Helpers │ ├── .gitkeep │ ├── CommentContentFormatter.swift │ ├── DailyDates.swift │ ├── Delay.swift │ └── RealmHelpers.swift ├── Models │ ├── .gitkeep │ ├── DailyRealmStore.swift │ ├── RealmModels.swift │ └── UserPreferences.swift ├── Protocols │ └── .gitkeep ├── Resources │ ├── .gitkeep │ ├── Base.lproj │ │ └── Localizable.strings │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-29.png │ │ │ ├── Icon-29@2x-1.png │ │ │ ├── Icon-29@2x.png │ │ │ ├── Icon-29@3x.png │ │ │ ├── Icon-40.png │ │ │ ├── Icon-40@2x-1.png │ │ │ ├── Icon-40@2x.png │ │ │ ├── Icon-40@3x.png │ │ │ ├── Icon-60@2x.png │ │ │ ├── Icon-60@3x.png │ │ │ ├── Icon-76.png │ │ │ └── Icon-76@2x.png │ ├── Info.plist │ ├── Localizations │ │ └── Swift-ZHI │ │ │ ├── zh-Hans.xliff │ │ │ └── zh-Hant.xliff │ ├── Nibs │ │ ├── .gitkeep │ │ ├── Base.lproj │ │ │ └── CommentSectionHeaderView.xib │ │ ├── DailySectionHeaderView.xib │ │ ├── LaunchScreen.xib │ │ ├── zh-Hans.lproj │ │ │ └── CommentSectionHeaderView.strings │ │ └── zh-Hant.lproj │ │ │ └── CommentSectionHeaderView.strings │ ├── Settings.bundle │ │ ├── Root.plist │ │ └── en.lproj │ │ │ └── Root.strings │ ├── Storyboards │ │ ├── .gitkeep │ │ ├── Base.lproj │ │ │ └── Main.storyboard │ │ ├── zh-Hans.lproj │ │ │ └── Main.strings │ │ └── zh-Hant.lproj │ │ │ └── Main.strings │ ├── zh-Hans.lproj │ │ ├── InfoPlist.strings │ │ └── Localizable.strings │ └── zh-Hant.lproj │ │ ├── InfoPlist.strings │ │ └── Localizable.strings ├── ViewModels │ └── .gitkeep └── Views │ ├── .gitkeep │ ├── CommentSectionHeaderView.swift │ ├── DailySectionHeaderView.swift │ └── LoadingCell.swift ├── SwiftDaily-ZhiHuTests ├── Info.plist ├── SwiftDaily_ZhiHuTests.swift ├── zh-Hans.lproj │ └── InfoPlist.strings └── zh-Hant.lproj │ └── InfoPlist.strings ├── SwiftDaily-ZhiHuUITests ├── Info.plist ├── SwiftDaily_ZhiHuUITests.swift ├── zh-Hans.lproj │ └── InfoPlist.strings └── zh-Hant.lproj │ └── InfoPlist.strings ├── Tests ├── .gitkeep ├── Helpers │ └── .gitkeep ├── Resources │ ├── .gitkeep │ └── Tests-Info.plist └── Tests │ └── .gitkeep └── bin ├── bump ├── merge ├── new_branch └── test /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj merge=union 2 | *.strings text diff 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X Finder 2 | .DS_Store 3 | 4 | # Xcode per-user config 5 | *.mode1 6 | *.mode1v3 7 | *.mode2v3 8 | *.perspective 9 | *.perspectivev3 10 | *.pbxuser 11 | xcuserdata 12 | *.xccheckout 13 | *.xcscmblueprint 14 | 15 | # Build products 16 | build/ 17 | *.o 18 | *.LinkFileList 19 | *.hmap 20 | 21 | # Automatic backup files 22 | *~.nib/ 23 | *.swp 24 | *~ 25 | *.dat 26 | *.dep 27 | 28 | # Cocoapods 29 | Pods 30 | 31 | # Carthage 32 | Carthage/Checkouts 33 | Carthage/Build 34 | 35 | # AppCode specific files 36 | .idea/ 37 | *.iml 38 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode7.2 3 | env: 4 | global: 5 | - LC_CTYPE=en_US.UTF-8 6 | - LANG=en_US.UTF-8 7 | script: 8 | - set -o pipefail 9 | - xcodebuild -version 10 | - xcodebuild -showsdks 11 | 12 | - xcodebuild -workspace Swift-ZHI.xcworkspace -scheme "Swift-ZHI" -destination "OS=9.2,name=iPhone 6s Plus" build | xcpretty -c 13 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "andreamazz/AMScrollingNavbar" "1.5.1" 2 | github "Alamofire/Alamofire" "1.3.1" 3 | github "robrix/Box" "1.2.2" 4 | github "thoughtbot/Runes" "v2.0.0" 5 | github "rs/SDWebImage" "3.7.3" 6 | github "realm/realm-cocoa" "v0.93.2" 7 | github "thoughtbot/Argo" "v1.0.4" 8 | github "NicholasTD07/SwiftDailyAPI" "9ce09b5061d4aa22188541de202f9427c16b6ec4" 9 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'xcpretty' 4 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | xcpretty (0.1.10) 5 | 6 | PLATFORMS 7 | ruby 8 | 9 | DEPENDENCIES 10 | xcpretty 11 | 12 | BUNDLED WITH 13 | 1.10.0 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Nicholas T. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '8.0' 2 | use_frameworks! 3 | 4 | pod 'SwiftDailyAPI', '~> 2.0' 5 | pod 'RealmSwift', '~> 0.97' 6 | pod 'SDWebImage', '~> 3.7.3' 7 | pod 'AMScrollingNavbar', '~> 1.5' 8 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (3.1.4) 3 | - AMScrollingNavbar (1.5.1) 4 | - Argo (2.2.0) 5 | - Realm (0.97.0): 6 | - Realm/Headers (= 0.97.0) 7 | - Realm/Headers (0.97.0) 8 | - RealmSwift (0.97.0): 9 | - Realm (= 0.97.0) 10 | - SDWebImage (3.7.4): 11 | - SDWebImage/Core (= 3.7.4) 12 | - SDWebImage/Core (3.7.4) 13 | - SwiftDailyAPI (2.0.1): 14 | - Alamofire (~> 3.0) 15 | - Argo (~> 2.2.0) 16 | 17 | DEPENDENCIES: 18 | - AMScrollingNavbar (~> 1.5) 19 | - RealmSwift (~> 0.97) 20 | - SDWebImage (~> 3.7.3) 21 | - SwiftDailyAPI (~> 2.0) 22 | 23 | SPEC CHECKSUMS: 24 | Alamofire: fbc829692f351fa1d8a31dd75fd7f7f56fea31fb 25 | AMScrollingNavbar: 767ed2db60d927a809bddff5d01b739b7020b12b 26 | Argo: 4f52a32b4cd913c204e5a45f420b3fccad3e3f24 27 | Realm: b498a6fb9d288b171ff98d0e2d4384c295be8990 28 | RealmSwift: d014810dec75c9a3f1a33926d2dabfed855c582c 29 | SDWebImage: 247edd7dd80dd6124197b0d6a17846fe9d1df968 30 | SwiftDailyAPI: 0ea377af335824d17564dc5972331c045095de86 31 | 32 | COCOAPODS: 0.39.0 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftDaily-ZhiHu 2 | 3 | [![Build Status](https://travis-ci.org/NicholasTD07/Swift-ZHI.svg?branch=develop)](https://travis-ci.org/NicholasTD07/Swift-ZHI) 4 | 5 | iOS ZhiHuDaily client, implemented in Swift. 6 | 7 | No need to wait for the loading screen of any News you'd like to read. You can 8 | save it first before you read it. It will be there for you. 9 | 10 | If you are reading ZhiHuDaily outside of China, the loading time for the content 11 | of a News is about 3-5 seconds. And on average a user reads 7-10 news a day. 12 | 13 | See [here](#user-content-there-is-already-an-official-zhihudaily-app-why-another) for detailed reason why I create this App. 14 | 15 | Showcase GIF 16 | 17 | ## Features 18 | 19 | ### Basics 20 | 21 | Basic functionality that the official App offers. 22 | 23 | * Show a list of latest daily news from Zhihu's API - DailyView 24 | * Infinite scrolling 25 | * Show news content in a detail view - NewsView 26 | 27 | ### Features I added 28 | 29 | * Offline storage/cache by using [Realm] 30 | * News list items - [`NewsMetaObject`](./SwiftDaily-ZhiHu/Models/RealmModels.swift) 31 | * News content - [`NewsObject`](./SwiftDaily-ZhiHu/Models/RealmModels.swift) 32 | 33 | ## Setup 34 | 35 | ```sh 36 | gem install cocoapods 37 | pod install 38 | ``` 39 | 40 | ### Minimum Requirement for Build Environment 41 | 42 | * Xcode 7.2 43 | * Swift 2.1 44 | 45 | ## Frameworks 46 | 47 | * [Realm] - Offline storage 48 | 49 | ### [SwiftDailyAPI] 50 | 51 | Framework I write to help myself building this App and possibly an Mac version 52 | of this App, also to help people who may want to build one themselves. 53 | 54 | #### Using 55 | 56 | * [Alamofire] - Elegant HTTP networking 57 | * [Argo] - Functional JSON parsing 58 | * [Quick] - Testing 59 | * [Nimble] - Matcher 60 | 61 | ## Documentation 62 | 63 | * [Use Protocol as Function Parameter in Swift](http://dev.nicktd.com/tldr/2015/06/08/use-protocol-in-swift-as-function-parameter.html) 64 | 65 | ### Planned 66 | 67 | * ZhiHuDaily's API 68 | * How to use mitmproxy to reverse engineer an App's API 69 | * Using Core Data with Swift and Protocol 70 | 71 | ## Q&A 72 | 73 | ### About the branch model for `git` 74 | 75 | * master - stable release 76 | * develop - mainly developing on this branch 77 | * experiment/ - testing things I am not sure whether it will work out or not 78 | * feature/ - building features 79 | 80 | ### There is already an official ZhihuDaily App. Why another? 81 | 82 | The official App can do lots of things, e.g. load the latest news, show detail news content, likes and comments, etc. 83 | 84 | However, it is missing some features that I would like to have. 85 | 86 | For example, as I mentioned at the top, because of network latency it takes 87 | about 3-5 seconds to load the content of a News, if you are using the official 88 | App outside of China. And I hate waiting. It's a waste of my time and my life. I 89 | want to be able to save multiple News' contents before I read it so that I don't 90 | have to wait for the loading screen. This is the main reason why I made this 91 | App, to save time and life, mine and also others'. 92 | 93 | Another thing is that the official App also tracks user behaviour and gives this 94 | information to a third party company in China. I just don't like it. 95 | 96 | ## APPENDIX 97 | 98 | ### ZhiHu 99 | 100 | From Wikipedia: 101 | "Zhihu" is a Chinese question-and-answer website. It has a similar product model as Quora. 102 |
103 | [link to wiki page](http://en.wikipedia.org/wiki/Zhihu) 104 | 105 | ### ZhihuDaily 106 | 107 | Daily highly-rated answers in ZhiHu, which are picked by editors. 108 | 109 | ### Previous Version 110 | 111 | [DailyNews](https://github.com/NicholasTD07/ios-playgrounds/tree/all-merged/ios8-restkit-zhihu) 112 | 113 | Screeshot: 114 | 115 | ![Daily App Showcase GIF](https://dl.dropboxusercontent.com/u/212792226/zhihu-daily-v1-take-3.gif) 116 | 117 | [Alamofire]: https://github.com/Alamofire/Alamofire 118 | [Argo]: https://github.com/thoughtbot/Argo 119 | [Quick]: https://github.com/Quick/Quick 120 | [Nimble]: https://github.com/Quick/Nimble 121 | [SwiftDailyAPI]: https://github.com/NicholasTD07/SwiftDailyAPI 122 | [Realm]: https://realm.io 123 | -------------------------------------------------------------------------------- /Swift-ZHI.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2D96E2C2697856C72E2AD083 /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4879E42D2BF192538AA36F94 /* Pods.framework */; }; 11 | 751788E91B3D47E700526889 /* DailyDates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 751788E81B3D47E700526889 /* DailyDates.swift */; }; 12 | 7529FA421B3E5EF5007D54A8 /* NewsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7529FA411B3E5EF5007D54A8 /* NewsViewController.swift */; }; 13 | 7529FA441B3E6B68007D54A8 /* RealmNewsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7529FA431B3E6B68007D54A8 /* RealmNewsViewController.swift */; }; 14 | 7530F3FD1B3D53D10035AD7A /* DailyTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7530F3FC1B3D53D10035AD7A /* DailyTableViewController.swift */; }; 15 | 7571839E1B4418ED00E345B3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 757183A01B4418ED00E345B3 /* Main.storyboard */; }; 16 | 757183A51B441CDA00E345B3 /* HideNavBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757183A41B441CDA00E345B3 /* HideNavBarViewController.swift */; }; 17 | 757FEB891B3ACF5600B4F327 /* SwiftDaily_ZhiHuTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757FEB881B3ACF5600B4F327 /* SwiftDaily_ZhiHuTests.swift */; }; 18 | 757FEBC91B3ACFE200B4F327 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757FEBA51B3ACFE100B4F327 /* AppDelegate.swift */; }; 19 | 757FEBCB1B3ACFE200B4F327 /* HidesHairLineUnderNavBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757FEBA71B3ACFE100B4F327 /* HidesHairLineUnderNavBarViewController.swift */; }; 20 | 757FEBCD1B3ACFE200B4F327 /* RealmDailyTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757FEBA91B3ACFE100B4F327 /* RealmDailyTableViewController.swift */; }; 21 | 757FEBD11B3ACFE200B4F327 /* RealmModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757FEBB01B3ACFE100B4F327 /* RealmModels.swift */; }; 22 | 757FEBD41B3ACFE200B4F327 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 757FEBB51B3ACFE100B4F327 /* Images.xcassets */; }; 23 | 757FEBD71B3ACFE200B4F327 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 757FEBB91B3ACFE200B4F327 /* LaunchScreen.xib */; }; 24 | 757FEBDA1B3ACFE200B4F327 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 757FEBBD1B3ACFE200B4F327 /* Settings.bundle */; }; 25 | 757FEBDF1B3ACFE200B4F327 /* DailySectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757FEBC51B3ACFE200B4F327 /* DailySectionHeaderView.swift */; }; 26 | 757FEBE01B3ACFE200B4F327 /* LoadingCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757FEBC61B3ACFE200B4F327 /* LoadingCell.swift */; }; 27 | 758872E91B6A12FC006ECF61 /* CommentSectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 758872E81B6A12FC006ECF61 /* CommentSectionHeaderView.swift */; }; 28 | 758872EE1B6B1026006ECF61 /* CommentContentFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 758872ED1B6B1026006ECF61 /* CommentContentFormatter.swift */; }; 29 | 758872FD1B70AE8B006ECF61 /* DailySectionHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 757FEBB81B3ACFE200B4F327 /* DailySectionHeaderView.xib */; }; 30 | 758872FE1B70AE94006ECF61 /* CommentSectionHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 758872F41B70ACF5006ECF61 /* CommentSectionHeaderView.xib */; }; 31 | 75AE08F21B65B12000462941 /* RealmCommentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75AE08F11B65B12000462941 /* RealmCommentViewController.swift */; }; 32 | 75B5F5FC1B44BA18001E1461 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 75D98CA81B44B93B001B4F9C /* InfoPlist.strings */; }; 33 | 75B5F60A1B44BF2C001E1461 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 75B5F6081B44BF2C001E1461 /* Localizable.strings */; }; 34 | 75CDD20C1B3BBB4A00343CAA /* DailyRealmStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75CDD20B1B3BBB4A00343CAA /* DailyRealmStore.swift */; }; 35 | 75CDD20E1B3BBD3900343CAA /* RealmHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75CDD20D1B3BBD3900343CAA /* RealmHelpers.swift */; }; 36 | 75D98CAB1B44B93C001B4F9C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 75D98CAD1B44B93C001B4F9C /* InfoPlist.strings */; }; 37 | 75EDD0C91B55340300C3DC41 /* UserPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EDD0C81B55340300C3DC41 /* UserPreferences.swift */; }; 38 | 75EF90F61B50E2D4006D008B /* Delay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EF90F51B50E2D4006D008B /* Delay.swift */; }; 39 | /* End PBXBuildFile section */ 40 | 41 | /* Begin PBXContainerItemProxy section */ 42 | 757FEB851B3ACF5600B4F327 /* PBXContainerItemProxy */ = { 43 | isa = PBXContainerItemProxy; 44 | containerPortal = 757FEB681B3ACF5600B4F327 /* Project object */; 45 | proxyType = 1; 46 | remoteGlobalIDString = 757FEB6F1B3ACF5600B4F327; 47 | remoteInfo = "SwiftDaily-ZhiHu"; 48 | }; 49 | /* End PBXContainerItemProxy section */ 50 | 51 | /* Begin PBXFileReference section */ 52 | 4879E42D2BF192538AA36F94 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | 6115B445073C89C047AB9EBB /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; 54 | 751788E81B3D47E700526889 /* DailyDates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DailyDates.swift; sourceTree = ""; }; 55 | 7529FA411B3E5EF5007D54A8 /* NewsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewsViewController.swift; sourceTree = ""; }; 56 | 7529FA431B3E6B68007D54A8 /* RealmNewsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmNewsViewController.swift; sourceTree = ""; }; 57 | 7530F3FC1B3D53D10035AD7A /* DailyTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DailyTableViewController.swift; sourceTree = ""; }; 58 | 75656D011B46549E007D11EA /* Realm.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Realm.framework; path = "Carthage/Checkouts/realm-cocoa/build/ios/swift/Realm.framework"; sourceTree = ""; }; 59 | 75656D021B46549E007D11EA /* RealmSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RealmSwift.framework; path = "Carthage/Checkouts/realm-cocoa/build/ios/swift/RealmSwift.framework"; sourceTree = ""; }; 60 | 75656D091B4659B4007D11EA /* Box.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Box.framework; path = Carthage/Build/iOS/Box.framework; sourceTree = ""; }; 61 | 757183A11B4418F300E345B3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 62 | 757183A41B441CDA00E345B3 /* HideNavBarViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HideNavBarViewController.swift; sourceTree = ""; }; 63 | 757FEB701B3ACF5600B4F327 /* Swift-ZHI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Swift-ZHI.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 64 | 757FEB841B3ACF5600B4F327 /* Swift-ZHITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Swift-ZHITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 65 | 757FEB881B3ACF5600B4F327 /* SwiftDaily_ZhiHuTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftDaily_ZhiHuTests.swift; sourceTree = ""; }; 66 | 757FEB8A1B3ACF5600B4F327 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 67 | 757FEB931B3ACF5600B4F327 /* SwiftDaily_ZhiHuUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftDaily_ZhiHuUITests.swift; sourceTree = ""; }; 68 | 757FEB951B3ACF5600B4F327 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 69 | 757FEBA51B3ACFE100B4F327 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 70 | 757FEBA71B3ACFE100B4F327 /* HidesHairLineUnderNavBarViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HidesHairLineUnderNavBarViewController.swift; sourceTree = ""; }; 71 | 757FEBA91B3ACFE100B4F327 /* RealmDailyTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmDailyTableViewController.swift; sourceTree = ""; }; 72 | 757FEBB01B3ACFE100B4F327 /* RealmModels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmModels.swift; sourceTree = ""; }; 73 | 757FEBB51B3ACFE100B4F327 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 74 | 757FEBB81B3ACFE200B4F327 /* DailySectionHeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DailySectionHeaderView.xib; sourceTree = ""; }; 75 | 757FEBB91B3ACFE200B4F327 /* LaunchScreen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LaunchScreen.xib; sourceTree = ""; }; 76 | 757FEBBD1B3ACFE200B4F327 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = ""; }; 77 | 757FEBC51B3ACFE200B4F327 /* DailySectionHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DailySectionHeaderView.swift; sourceTree = ""; }; 78 | 757FEBC61B3ACFE200B4F327 /* LoadingCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadingCell.swift; sourceTree = ""; }; 79 | 757FEBE11B3AD0C800B4F327 /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = Carthage/Build/iOS/Alamofire.framework; sourceTree = ""; }; 80 | 757FEBE21B3AD0C800B4F327 /* AMScrollingNavbar.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AMScrollingNavbar.framework; path = Carthage/Build/iOS/AMScrollingNavbar.framework; sourceTree = ""; }; 81 | 757FEBE31B3AD0C800B4F327 /* Argo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Argo.framework; path = Carthage/Build/iOS/Argo.framework; sourceTree = ""; }; 82 | 757FEBE41B3AD0C800B4F327 /* Runes.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Runes.framework; path = Carthage/Build/iOS/Runes.framework; sourceTree = ""; }; 83 | 757FEBE51B3AD0C800B4F327 /* SwiftDailyAPI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftDailyAPI.framework; path = Carthage/Build/iOS/SwiftDailyAPI.framework; sourceTree = ""; }; 84 | 758872E81B6A12FC006ECF61 /* CommentSectionHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommentSectionHeaderView.swift; sourceTree = ""; }; 85 | 758872ED1B6B1026006ECF61 /* CommentContentFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommentContentFormatter.swift; sourceTree = ""; }; 86 | 758872F31B70ACF5006ECF61 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/CommentSectionHeaderView.xib; sourceTree = ""; }; 87 | 758872F61B70AD04006ECF61 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/CommentSectionHeaderView.strings"; sourceTree = ""; }; 88 | 758872F81B70AD05006ECF61 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/CommentSectionHeaderView.strings"; sourceTree = ""; }; 89 | 758872FA1B70AD6A006ECF61 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Main.strings"; sourceTree = ""; }; 90 | 758872FC1B70AD71006ECF61 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Main.strings"; sourceTree = ""; }; 91 | 758F0B921B9888C8006B6842 /* WebImage.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebImage.framework; path = Carthage/Build/iOS/WebImage.framework; sourceTree = ""; }; 92 | 75AE08F11B65B12000462941 /* RealmCommentViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmCommentViewController.swift; sourceTree = ""; }; 93 | 75B5F5F71B44B97D001E1461 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/InfoPlist.strings"; sourceTree = ""; }; 94 | 75B5F5F81B44B97D001E1461 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/InfoPlist.strings"; sourceTree = ""; }; 95 | 75B5F5F91B44B97E001E1461 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/InfoPlist.strings"; sourceTree = ""; }; 96 | 75B5F6091B44BF2C001E1461 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = "SwiftDaily-ZhiHu/Resources/Base.lproj/Localizable.strings"; sourceTree = SOURCE_ROOT; }; 97 | 75B5F60B1B44BF37001E1461 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "SwiftDaily-ZhiHu/Resources/zh-Hans.lproj/Localizable.strings"; sourceTree = SOURCE_ROOT; }; 98 | 75B5F60C1B44BF3E001E1461 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "SwiftDaily-ZhiHu/Resources/zh-Hant.lproj/Localizable.strings"; sourceTree = SOURCE_ROOT; }; 99 | 75CDD20B1B3BBB4A00343CAA /* DailyRealmStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DailyRealmStore.swift; sourceTree = ""; }; 100 | 75CDD20D1B3BBD3900343CAA /* RealmHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmHelpers.swift; sourceTree = ""; }; 101 | 75D98C9D1B44B704001B4F9C /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 102 | 75D98CA71B44B93B001B4F9C /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = ""; }; 103 | 75D98CAC1B44B93C001B4F9C /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = ""; }; 104 | 75D98CB11B44B93C001B4F9C /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = ""; }; 105 | 75E2E5AE1B46B24200E26DD0 /* zh-Hans.xliff */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = "zh-Hans.xliff"; sourceTree = ""; }; 106 | 75E2E5AF1B46B24200E26DD0 /* zh-Hant.xliff */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = "zh-Hant.xliff"; sourceTree = ""; }; 107 | 75EDD0C81B55340300C3DC41 /* UserPreferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserPreferences.swift; sourceTree = ""; }; 108 | 75EF90F51B50E2D4006D008B /* Delay.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Delay.swift; sourceTree = ""; }; 109 | 83EB994A3DBD56F213E19F2B /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; 110 | /* End PBXFileReference section */ 111 | 112 | /* Begin PBXFrameworksBuildPhase section */ 113 | 757FEB6D1B3ACF5600B4F327 /* Frameworks */ = { 114 | isa = PBXFrameworksBuildPhase; 115 | buildActionMask = 2147483647; 116 | files = ( 117 | 2D96E2C2697856C72E2AD083 /* Pods.framework in Frameworks */, 118 | ); 119 | runOnlyForDeploymentPostprocessing = 0; 120 | }; 121 | 757FEB811B3ACF5600B4F327 /* Frameworks */ = { 122 | isa = PBXFrameworksBuildPhase; 123 | buildActionMask = 2147483647; 124 | files = ( 125 | ); 126 | runOnlyForDeploymentPostprocessing = 0; 127 | }; 128 | /* End PBXFrameworksBuildPhase section */ 129 | 130 | /* Begin PBXGroup section */ 131 | 757FEB671B3ACF5600B4F327 = { 132 | isa = PBXGroup; 133 | children = ( 134 | 757FEBA11B3ACFE100B4F327 /* SwiftDaily-ZhiHu */, 135 | 757FEB871B3ACF5600B4F327 /* SwiftDaily-ZhiHuTests */, 136 | 757FEB921B3ACF5600B4F327 /* SwiftDaily-ZhiHuUITests */, 137 | 757FEBEB1B3AD0D300B4F327 /* Frameworks */, 138 | 757FEB711B3ACF5600B4F327 /* Products */, 139 | CC2923247F366A7C2E322078 /* Pods */, 140 | ); 141 | sourceTree = ""; 142 | }; 143 | 757FEB711B3ACF5600B4F327 /* Products */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 757FEB701B3ACF5600B4F327 /* Swift-ZHI.app */, 147 | 757FEB841B3ACF5600B4F327 /* Swift-ZHITests.xctest */, 148 | ); 149 | name = Products; 150 | sourceTree = ""; 151 | }; 152 | 757FEB871B3ACF5600B4F327 /* SwiftDaily-ZhiHuTests */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | 75D98CAD1B44B93C001B4F9C /* InfoPlist.strings */, 156 | 757FEB881B3ACF5600B4F327 /* SwiftDaily_ZhiHuTests.swift */, 157 | 757FEB8A1B3ACF5600B4F327 /* Info.plist */, 158 | ); 159 | path = "SwiftDaily-ZhiHuTests"; 160 | sourceTree = ""; 161 | }; 162 | 757FEB921B3ACF5600B4F327 /* SwiftDaily-ZhiHuUITests */ = { 163 | isa = PBXGroup; 164 | children = ( 165 | 75D98CB21B44B93C001B4F9C /* InfoPlist.strings */, 166 | 757FEB931B3ACF5600B4F327 /* SwiftDaily_ZhiHuUITests.swift */, 167 | 757FEB951B3ACF5600B4F327 /* Info.plist */, 168 | ); 169 | path = "SwiftDaily-ZhiHuUITests"; 170 | sourceTree = ""; 171 | }; 172 | 757FEBA11B3ACFE100B4F327 /* SwiftDaily-ZhiHu */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | 757FEBA31B3ACFE100B4F327 /* Controllers */, 176 | 757FEBAA1B3ACFE100B4F327 /* Extensions */, 177 | 757FEBAC1B3ACFE100B4F327 /* Helpers */, 178 | 757FEBAE1B3ACFE100B4F327 /* Models */, 179 | 757FEBB11B3ACFE100B4F327 /* Protocols */, 180 | 757FEBB31B3ACFE100B4F327 /* Resources */, 181 | 757FEBC11B3ACFE200B4F327 /* ViewModels */, 182 | 757FEBC31B3ACFE200B4F327 /* Views */, 183 | ); 184 | path = "SwiftDaily-ZhiHu"; 185 | sourceTree = ""; 186 | }; 187 | 757FEBA31B3ACFE100B4F327 /* Controllers */ = { 188 | isa = PBXGroup; 189 | children = ( 190 | 757FEBA51B3ACFE100B4F327 /* AppDelegate.swift */, 191 | 7530F3FC1B3D53D10035AD7A /* DailyTableViewController.swift */, 192 | 757183A41B441CDA00E345B3 /* HideNavBarViewController.swift */, 193 | 757FEBA71B3ACFE100B4F327 /* HidesHairLineUnderNavBarViewController.swift */, 194 | 7529FA411B3E5EF5007D54A8 /* NewsViewController.swift */, 195 | 757FEBA91B3ACFE100B4F327 /* RealmDailyTableViewController.swift */, 196 | 7529FA431B3E6B68007D54A8 /* RealmNewsViewController.swift */, 197 | 75AE08F11B65B12000462941 /* RealmCommentViewController.swift */, 198 | ); 199 | path = Controllers; 200 | sourceTree = ""; 201 | }; 202 | 757FEBAA1B3ACFE100B4F327 /* Extensions */ = { 203 | isa = PBXGroup; 204 | children = ( 205 | ); 206 | path = Extensions; 207 | sourceTree = ""; 208 | }; 209 | 757FEBAC1B3ACFE100B4F327 /* Helpers */ = { 210 | isa = PBXGroup; 211 | children = ( 212 | 751788E81B3D47E700526889 /* DailyDates.swift */, 213 | 75CDD20D1B3BBD3900343CAA /* RealmHelpers.swift */, 214 | 75EF90F51B50E2D4006D008B /* Delay.swift */, 215 | 758872ED1B6B1026006ECF61 /* CommentContentFormatter.swift */, 216 | ); 217 | path = Helpers; 218 | sourceTree = ""; 219 | }; 220 | 757FEBAE1B3ACFE100B4F327 /* Models */ = { 221 | isa = PBXGroup; 222 | children = ( 223 | 757FEBB01B3ACFE100B4F327 /* RealmModels.swift */, 224 | 75CDD20B1B3BBB4A00343CAA /* DailyRealmStore.swift */, 225 | 75EDD0C81B55340300C3DC41 /* UserPreferences.swift */, 226 | ); 227 | path = Models; 228 | sourceTree = ""; 229 | }; 230 | 757FEBB11B3ACFE100B4F327 /* Protocols */ = { 231 | isa = PBXGroup; 232 | children = ( 233 | ); 234 | path = Protocols; 235 | sourceTree = ""; 236 | }; 237 | 757FEBB31B3ACFE100B4F327 /* Resources */ = { 238 | isa = PBXGroup; 239 | children = ( 240 | 75D98C9D1B44B704001B4F9C /* Info.plist */, 241 | 757FEBB51B3ACFE100B4F327 /* Images.xcassets */, 242 | 75D98C9F1B44B920001B4F9C /* Localizations */, 243 | 757FEBB61B3ACFE100B4F327 /* Nibs */, 244 | 757FEBBD1B3ACFE200B4F327 /* Settings.bundle */, 245 | 757FEBBE1B3ACFE200B4F327 /* Storyboards */, 246 | ); 247 | path = Resources; 248 | sourceTree = ""; 249 | }; 250 | 757FEBB61B3ACFE100B4F327 /* Nibs */ = { 251 | isa = PBXGroup; 252 | children = ( 253 | 757FEBB81B3ACFE200B4F327 /* DailySectionHeaderView.xib */, 254 | 757FEBB91B3ACFE200B4F327 /* LaunchScreen.xib */, 255 | 758872F41B70ACF5006ECF61 /* CommentSectionHeaderView.xib */, 256 | ); 257 | path = Nibs; 258 | sourceTree = ""; 259 | }; 260 | 757FEBBE1B3ACFE200B4F327 /* Storyboards */ = { 261 | isa = PBXGroup; 262 | children = ( 263 | 757183A01B4418ED00E345B3 /* Main.storyboard */, 264 | ); 265 | path = Storyboards; 266 | sourceTree = ""; 267 | }; 268 | 757FEBC11B3ACFE200B4F327 /* ViewModels */ = { 269 | isa = PBXGroup; 270 | children = ( 271 | ); 272 | path = ViewModels; 273 | sourceTree = ""; 274 | }; 275 | 757FEBC31B3ACFE200B4F327 /* Views */ = { 276 | isa = PBXGroup; 277 | children = ( 278 | 757FEBC51B3ACFE200B4F327 /* DailySectionHeaderView.swift */, 279 | 757FEBC61B3ACFE200B4F327 /* LoadingCell.swift */, 280 | 758872E81B6A12FC006ECF61 /* CommentSectionHeaderView.swift */, 281 | ); 282 | path = Views; 283 | sourceTree = ""; 284 | }; 285 | 757FEBEB1B3AD0D300B4F327 /* Frameworks */ = { 286 | isa = PBXGroup; 287 | children = ( 288 | 758F0B921B9888C8006B6842 /* WebImage.framework */, 289 | 757FEBE31B3AD0C800B4F327 /* Argo.framework */, 290 | 75656D091B4659B4007D11EA /* Box.framework */, 291 | 75656D011B46549E007D11EA /* Realm.framework */, 292 | 75656D021B46549E007D11EA /* RealmSwift.framework */, 293 | 757FEBE11B3AD0C800B4F327 /* Alamofire.framework */, 294 | 757FEBE21B3AD0C800B4F327 /* AMScrollingNavbar.framework */, 295 | 757FEBE41B3AD0C800B4F327 /* Runes.framework */, 296 | 757FEBE51B3AD0C800B4F327 /* SwiftDailyAPI.framework */, 297 | 4879E42D2BF192538AA36F94 /* Pods.framework */, 298 | ); 299 | name = Frameworks; 300 | sourceTree = ""; 301 | }; 302 | 75D98C9F1B44B920001B4F9C /* Localizations */ = { 303 | isa = PBXGroup; 304 | children = ( 305 | 75D98CA81B44B93B001B4F9C /* InfoPlist.strings */, 306 | 75B5F6081B44BF2C001E1461 /* Localizable.strings */, 307 | 75E2E5AD1B46B24200E26DD0 /* Swift-ZHI */, 308 | ); 309 | path = Localizations; 310 | sourceTree = ""; 311 | }; 312 | 75E2E5AD1B46B24200E26DD0 /* Swift-ZHI */ = { 313 | isa = PBXGroup; 314 | children = ( 315 | 75E2E5AE1B46B24200E26DD0 /* zh-Hans.xliff */, 316 | 75E2E5AF1B46B24200E26DD0 /* zh-Hant.xliff */, 317 | ); 318 | path = "Swift-ZHI"; 319 | sourceTree = ""; 320 | }; 321 | CC2923247F366A7C2E322078 /* Pods */ = { 322 | isa = PBXGroup; 323 | children = ( 324 | 83EB994A3DBD56F213E19F2B /* Pods.debug.xcconfig */, 325 | 6115B445073C89C047AB9EBB /* Pods.release.xcconfig */, 326 | ); 327 | name = Pods; 328 | sourceTree = ""; 329 | }; 330 | /* End PBXGroup section */ 331 | 332 | /* Begin PBXNativeTarget section */ 333 | 757FEB6F1B3ACF5600B4F327 /* Swift-ZHI */ = { 334 | isa = PBXNativeTarget; 335 | buildConfigurationList = 757FEB981B3ACF5600B4F327 /* Build configuration list for PBXNativeTarget "Swift-ZHI" */; 336 | buildPhases = ( 337 | DBD108EAE0BDD61F51EDD061 /* Check Pods Manifest.lock */, 338 | 757FEB6C1B3ACF5600B4F327 /* Sources */, 339 | 757FEB6D1B3ACF5600B4F327 /* Frameworks */, 340 | 757FEB6E1B3ACF5600B4F327 /* Resources */, 341 | 75B5F60D1B44BF70001E1461 /* genstrings */, 342 | D95A75E2DF6A4894F1E2968D /* Embed Pods Frameworks */, 343 | 8CA5F546ECEC4BDE500DFC3E /* Copy Pods Resources */, 344 | ); 345 | buildRules = ( 346 | ); 347 | dependencies = ( 348 | ); 349 | name = "Swift-ZHI"; 350 | productName = "SwiftDaily-ZhiHu"; 351 | productReference = 757FEB701B3ACF5600B4F327 /* Swift-ZHI.app */; 352 | productType = "com.apple.product-type.application"; 353 | }; 354 | 757FEB831B3ACF5600B4F327 /* Swift-ZHITests */ = { 355 | isa = PBXNativeTarget; 356 | buildConfigurationList = 757FEB9B1B3ACF5600B4F327 /* Build configuration list for PBXNativeTarget "Swift-ZHITests" */; 357 | buildPhases = ( 358 | 757FEB801B3ACF5600B4F327 /* Sources */, 359 | 757FEB811B3ACF5600B4F327 /* Frameworks */, 360 | 757FEB821B3ACF5600B4F327 /* Resources */, 361 | ); 362 | buildRules = ( 363 | ); 364 | dependencies = ( 365 | 757FEB861B3ACF5600B4F327 /* PBXTargetDependency */, 366 | ); 367 | name = "Swift-ZHITests"; 368 | productName = "SwiftDaily-ZhiHuTests"; 369 | productReference = 757FEB841B3ACF5600B4F327 /* Swift-ZHITests.xctest */; 370 | productType = "com.apple.product-type.bundle.unit-test"; 371 | }; 372 | /* End PBXNativeTarget section */ 373 | 374 | /* Begin PBXProject section */ 375 | 757FEB681B3ACF5600B4F327 /* Project object */ = { 376 | isa = PBXProject; 377 | attributes = { 378 | LastSwiftMigration = 0730; 379 | LastSwiftUpdateCheck = 0700; 380 | LastUpgradeCheck = 0700; 381 | ORGANIZATIONNAME = nickTD; 382 | TargetAttributes = { 383 | 757FEB6F1B3ACF5600B4F327 = { 384 | CreatedOnToolsVersion = 7.0; 385 | }; 386 | 757FEB831B3ACF5600B4F327 = { 387 | CreatedOnToolsVersion = 7.0; 388 | TestTargetID = 757FEB6F1B3ACF5600B4F327; 389 | }; 390 | }; 391 | }; 392 | buildConfigurationList = 757FEB6B1B3ACF5600B4F327 /* Build configuration list for PBXProject "Swift-ZHI" */; 393 | compatibilityVersion = "Xcode 3.2"; 394 | developmentRegion = English; 395 | hasScannedForEncodings = 0; 396 | knownRegions = ( 397 | en, 398 | Base, 399 | "zh-Hans", 400 | "zh-Hant", 401 | ); 402 | mainGroup = 757FEB671B3ACF5600B4F327; 403 | productRefGroup = 757FEB711B3ACF5600B4F327 /* Products */; 404 | projectDirPath = ""; 405 | projectRoot = ""; 406 | targets = ( 407 | 757FEB6F1B3ACF5600B4F327 /* Swift-ZHI */, 408 | 757FEB831B3ACF5600B4F327 /* Swift-ZHITests */, 409 | ); 410 | }; 411 | /* End PBXProject section */ 412 | 413 | /* Begin PBXResourcesBuildPhase section */ 414 | 757FEB6E1B3ACF5600B4F327 /* Resources */ = { 415 | isa = PBXResourcesBuildPhase; 416 | buildActionMask = 2147483647; 417 | files = ( 418 | 758872FE1B70AE94006ECF61 /* CommentSectionHeaderView.xib in Resources */, 419 | 758872FD1B70AE8B006ECF61 /* DailySectionHeaderView.xib in Resources */, 420 | 75B5F5FC1B44BA18001E1461 /* InfoPlist.strings in Resources */, 421 | 757FEBD41B3ACFE200B4F327 /* Images.xcassets in Resources */, 422 | 75B5F60A1B44BF2C001E1461 /* Localizable.strings in Resources */, 423 | 757FEBDA1B3ACFE200B4F327 /* Settings.bundle in Resources */, 424 | 7571839E1B4418ED00E345B3 /* Main.storyboard in Resources */, 425 | 757FEBD71B3ACFE200B4F327 /* LaunchScreen.xib in Resources */, 426 | ); 427 | runOnlyForDeploymentPostprocessing = 0; 428 | }; 429 | 757FEB821B3ACF5600B4F327 /* Resources */ = { 430 | isa = PBXResourcesBuildPhase; 431 | buildActionMask = 2147483647; 432 | files = ( 433 | 75D98CAB1B44B93C001B4F9C /* InfoPlist.strings in Resources */, 434 | ); 435 | runOnlyForDeploymentPostprocessing = 0; 436 | }; 437 | /* End PBXResourcesBuildPhase section */ 438 | 439 | /* Begin PBXShellScriptBuildPhase section */ 440 | 75B5F60D1B44BF70001E1461 /* genstrings */ = { 441 | isa = PBXShellScriptBuildPhase; 442 | buildActionMask = 2147483647; 443 | files = ( 444 | ); 445 | inputPaths = ( 446 | ); 447 | name = genstrings; 448 | outputPaths = ( 449 | ); 450 | runOnlyForDeploymentPostprocessing = 0; 451 | shellPath = /bin/sh; 452 | shellScript = "cd $PROJECT_DIR && find ./SwiftDaily-Zhihu -name \\*.swift | xargs genstrings -o SwiftDaily-ZhiHu/Resources/Base.lproj/"; 453 | }; 454 | 8CA5F546ECEC4BDE500DFC3E /* Copy Pods Resources */ = { 455 | isa = PBXShellScriptBuildPhase; 456 | buildActionMask = 2147483647; 457 | files = ( 458 | ); 459 | inputPaths = ( 460 | ); 461 | name = "Copy Pods Resources"; 462 | outputPaths = ( 463 | ); 464 | runOnlyForDeploymentPostprocessing = 0; 465 | shellPath = /bin/sh; 466 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; 467 | showEnvVarsInLog = 0; 468 | }; 469 | D95A75E2DF6A4894F1E2968D /* Embed Pods Frameworks */ = { 470 | isa = PBXShellScriptBuildPhase; 471 | buildActionMask = 2147483647; 472 | files = ( 473 | ); 474 | inputPaths = ( 475 | ); 476 | name = "Embed Pods Frameworks"; 477 | outputPaths = ( 478 | ); 479 | runOnlyForDeploymentPostprocessing = 0; 480 | shellPath = /bin/sh; 481 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n"; 482 | showEnvVarsInLog = 0; 483 | }; 484 | DBD108EAE0BDD61F51EDD061 /* Check Pods Manifest.lock */ = { 485 | isa = PBXShellScriptBuildPhase; 486 | buildActionMask = 2147483647; 487 | files = ( 488 | ); 489 | inputPaths = ( 490 | ); 491 | name = "Check Pods Manifest.lock"; 492 | outputPaths = ( 493 | ); 494 | runOnlyForDeploymentPostprocessing = 0; 495 | shellPath = /bin/sh; 496 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 497 | showEnvVarsInLog = 0; 498 | }; 499 | /* End PBXShellScriptBuildPhase section */ 500 | 501 | /* Begin PBXSourcesBuildPhase section */ 502 | 757FEB6C1B3ACF5600B4F327 /* Sources */ = { 503 | isa = PBXSourcesBuildPhase; 504 | buildActionMask = 2147483647; 505 | files = ( 506 | 751788E91B3D47E700526889 /* DailyDates.swift in Sources */, 507 | 7529FA441B3E6B68007D54A8 /* RealmNewsViewController.swift in Sources */, 508 | 757FEBCD1B3ACFE200B4F327 /* RealmDailyTableViewController.swift in Sources */, 509 | 757FEBCB1B3ACFE200B4F327 /* HidesHairLineUnderNavBarViewController.swift in Sources */, 510 | 757FEBE01B3ACFE200B4F327 /* LoadingCell.swift in Sources */, 511 | 75CDD20C1B3BBB4A00343CAA /* DailyRealmStore.swift in Sources */, 512 | 757FEBC91B3ACFE200B4F327 /* AppDelegate.swift in Sources */, 513 | 7529FA421B3E5EF5007D54A8 /* NewsViewController.swift in Sources */, 514 | 758872E91B6A12FC006ECF61 /* CommentSectionHeaderView.swift in Sources */, 515 | 75CDD20E1B3BBD3900343CAA /* RealmHelpers.swift in Sources */, 516 | 757FEBD11B3ACFE200B4F327 /* RealmModels.swift in Sources */, 517 | 75EDD0C91B55340300C3DC41 /* UserPreferences.swift in Sources */, 518 | 758872EE1B6B1026006ECF61 /* CommentContentFormatter.swift in Sources */, 519 | 757FEBDF1B3ACFE200B4F327 /* DailySectionHeaderView.swift in Sources */, 520 | 75AE08F21B65B12000462941 /* RealmCommentViewController.swift in Sources */, 521 | 75EF90F61B50E2D4006D008B /* Delay.swift in Sources */, 522 | 757183A51B441CDA00E345B3 /* HideNavBarViewController.swift in Sources */, 523 | 7530F3FD1B3D53D10035AD7A /* DailyTableViewController.swift in Sources */, 524 | ); 525 | runOnlyForDeploymentPostprocessing = 0; 526 | }; 527 | 757FEB801B3ACF5600B4F327 /* Sources */ = { 528 | isa = PBXSourcesBuildPhase; 529 | buildActionMask = 2147483647; 530 | files = ( 531 | 757FEB891B3ACF5600B4F327 /* SwiftDaily_ZhiHuTests.swift in Sources */, 532 | ); 533 | runOnlyForDeploymentPostprocessing = 0; 534 | }; 535 | /* End PBXSourcesBuildPhase section */ 536 | 537 | /* Begin PBXTargetDependency section */ 538 | 757FEB861B3ACF5600B4F327 /* PBXTargetDependency */ = { 539 | isa = PBXTargetDependency; 540 | target = 757FEB6F1B3ACF5600B4F327 /* Swift-ZHI */; 541 | targetProxy = 757FEB851B3ACF5600B4F327 /* PBXContainerItemProxy */; 542 | }; 543 | /* End PBXTargetDependency section */ 544 | 545 | /* Begin PBXVariantGroup section */ 546 | 757183A01B4418ED00E345B3 /* Main.storyboard */ = { 547 | isa = PBXVariantGroup; 548 | children = ( 549 | 757183A11B4418F300E345B3 /* Base */, 550 | 758872FA1B70AD6A006ECF61 /* zh-Hans */, 551 | 758872FC1B70AD71006ECF61 /* zh-Hant */, 552 | ); 553 | name = Main.storyboard; 554 | sourceTree = ""; 555 | }; 556 | 758872F41B70ACF5006ECF61 /* CommentSectionHeaderView.xib */ = { 557 | isa = PBXVariantGroup; 558 | children = ( 559 | 758872F31B70ACF5006ECF61 /* Base */, 560 | 758872F61B70AD04006ECF61 /* zh-Hans */, 561 | 758872F81B70AD05006ECF61 /* zh-Hant */, 562 | ); 563 | name = CommentSectionHeaderView.xib; 564 | sourceTree = ""; 565 | }; 566 | 75B5F6081B44BF2C001E1461 /* Localizable.strings */ = { 567 | isa = PBXVariantGroup; 568 | children = ( 569 | 75B5F6091B44BF2C001E1461 /* Base */, 570 | 75B5F60B1B44BF37001E1461 /* zh-Hans */, 571 | 75B5F60C1B44BF3E001E1461 /* zh-Hant */, 572 | ); 573 | name = Localizable.strings; 574 | sourceTree = ""; 575 | }; 576 | 75D98CA81B44B93B001B4F9C /* InfoPlist.strings */ = { 577 | isa = PBXVariantGroup; 578 | children = ( 579 | 75D98CA71B44B93B001B4F9C /* zh-Hans */, 580 | 75B5F5F71B44B97D001E1461 /* zh-Hant */, 581 | ); 582 | name = InfoPlist.strings; 583 | path = ..; 584 | sourceTree = ""; 585 | }; 586 | 75D98CAD1B44B93C001B4F9C /* InfoPlist.strings */ = { 587 | isa = PBXVariantGroup; 588 | children = ( 589 | 75D98CAC1B44B93C001B4F9C /* zh-Hans */, 590 | 75B5F5F81B44B97D001E1461 /* zh-Hant */, 591 | ); 592 | name = InfoPlist.strings; 593 | sourceTree = ""; 594 | }; 595 | 75D98CB21B44B93C001B4F9C /* InfoPlist.strings */ = { 596 | isa = PBXVariantGroup; 597 | children = ( 598 | 75D98CB11B44B93C001B4F9C /* zh-Hans */, 599 | 75B5F5F91B44B97E001E1461 /* zh-Hant */, 600 | ); 601 | name = InfoPlist.strings; 602 | sourceTree = ""; 603 | }; 604 | /* End PBXVariantGroup section */ 605 | 606 | /* Begin XCBuildConfiguration section */ 607 | 757FEB961B3ACF5600B4F327 /* Debug */ = { 608 | isa = XCBuildConfiguration; 609 | buildSettings = { 610 | ALWAYS_SEARCH_USER_PATHS = NO; 611 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 612 | CLANG_CXX_LIBRARY = "libc++"; 613 | CLANG_ENABLE_MODULES = YES; 614 | CLANG_ENABLE_OBJC_ARC = YES; 615 | CLANG_WARN_BOOL_CONVERSION = YES; 616 | CLANG_WARN_CONSTANT_CONVERSION = YES; 617 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 618 | CLANG_WARN_EMPTY_BODY = YES; 619 | CLANG_WARN_ENUM_CONVERSION = YES; 620 | CLANG_WARN_INT_CONVERSION = YES; 621 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 622 | CLANG_WARN_UNREACHABLE_CODE = YES; 623 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 624 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 625 | COPY_PHASE_STRIP = NO; 626 | DEBUG_INFORMATION_FORMAT = dwarf; 627 | ENABLE_STRICT_OBJC_MSGSEND = YES; 628 | ENABLE_TESTABILITY = YES; 629 | FRAMEWORK_SEARCH_PATHS = ""; 630 | GCC_C_LANGUAGE_STANDARD = gnu99; 631 | GCC_DYNAMIC_NO_PIC = NO; 632 | GCC_NO_COMMON_BLOCKS = YES; 633 | GCC_OPTIMIZATION_LEVEL = 0; 634 | GCC_PREPROCESSOR_DEFINITIONS = ( 635 | "DEBUG=1", 636 | "$(inherited)", 637 | ); 638 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 639 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 640 | GCC_WARN_UNDECLARED_SELECTOR = YES; 641 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 642 | GCC_WARN_UNUSED_FUNCTION = YES; 643 | GCC_WARN_UNUSED_VARIABLE = YES; 644 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 645 | MTL_ENABLE_DEBUG_INFO = YES; 646 | ONLY_ACTIVE_ARCH = YES; 647 | SDKROOT = iphoneos; 648 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 649 | }; 650 | name = Debug; 651 | }; 652 | 757FEB971B3ACF5600B4F327 /* Release */ = { 653 | isa = XCBuildConfiguration; 654 | buildSettings = { 655 | ALWAYS_SEARCH_USER_PATHS = NO; 656 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 657 | CLANG_CXX_LIBRARY = "libc++"; 658 | CLANG_ENABLE_MODULES = YES; 659 | CLANG_ENABLE_OBJC_ARC = YES; 660 | CLANG_WARN_BOOL_CONVERSION = YES; 661 | CLANG_WARN_CONSTANT_CONVERSION = YES; 662 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 663 | CLANG_WARN_EMPTY_BODY = YES; 664 | CLANG_WARN_ENUM_CONVERSION = YES; 665 | CLANG_WARN_INT_CONVERSION = YES; 666 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 667 | CLANG_WARN_UNREACHABLE_CODE = YES; 668 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 669 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 670 | COPY_PHASE_STRIP = NO; 671 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 672 | ENABLE_NS_ASSERTIONS = NO; 673 | ENABLE_STRICT_OBJC_MSGSEND = YES; 674 | FRAMEWORK_SEARCH_PATHS = ""; 675 | GCC_C_LANGUAGE_STANDARD = gnu99; 676 | GCC_NO_COMMON_BLOCKS = YES; 677 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 678 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 679 | GCC_WARN_UNDECLARED_SELECTOR = YES; 680 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 681 | GCC_WARN_UNUSED_FUNCTION = YES; 682 | GCC_WARN_UNUSED_VARIABLE = YES; 683 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 684 | MTL_ENABLE_DEBUG_INFO = NO; 685 | SDKROOT = iphoneos; 686 | VALIDATE_PRODUCT = YES; 687 | }; 688 | name = Release; 689 | }; 690 | 757FEB991B3ACF5600B4F327 /* Debug */ = { 691 | isa = XCBuildConfiguration; 692 | baseConfigurationReference = 83EB994A3DBD56F213E19F2B /* Pods.debug.xcconfig */; 693 | buildSettings = { 694 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 695 | ENABLE_BITCODE = NO; 696 | FRAMEWORK_SEARCH_PATHS = ""; 697 | INFOPLIST_FILE = "$(SRCROOT)/SwiftDaily-ZhiHu/Resources/Info.plist"; 698 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 699 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 700 | PRODUCT_BUNDLE_IDENTIFIER = "com.nickTD.Swift-ZHI"; 701 | PRODUCT_NAME = "Swift-ZHI"; 702 | TARGETED_DEVICE_FAMILY = "1,2"; 703 | }; 704 | name = Debug; 705 | }; 706 | 757FEB9A1B3ACF5600B4F327 /* Release */ = { 707 | isa = XCBuildConfiguration; 708 | baseConfigurationReference = 6115B445073C89C047AB9EBB /* Pods.release.xcconfig */; 709 | buildSettings = { 710 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 711 | ENABLE_BITCODE = NO; 712 | FRAMEWORK_SEARCH_PATHS = ""; 713 | INFOPLIST_FILE = "$(SRCROOT)/SwiftDaily-ZhiHu/Resources/Info.plist"; 714 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 715 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 716 | PRODUCT_BUNDLE_IDENTIFIER = "com.nickTD.Swift-ZHI"; 717 | PRODUCT_NAME = "Swift-ZHI"; 718 | TARGETED_DEVICE_FAMILY = "1,2"; 719 | }; 720 | name = Release; 721 | }; 722 | 757FEB9C1B3ACF5600B4F327 /* Debug */ = { 723 | isa = XCBuildConfiguration; 724 | buildSettings = { 725 | BUNDLE_LOADER = "$(TEST_HOST)"; 726 | INFOPLIST_FILE = "SwiftDaily-ZhiHuTests/Info.plist"; 727 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 728 | PRODUCT_BUNDLE_IDENTIFIER = "com.nickTD.SwiftDaily-ZhiHuTests"; 729 | PRODUCT_NAME = "Swift-ZHITests"; 730 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Swift-ZHI.app/Swift-ZHI"; 731 | }; 732 | name = Debug; 733 | }; 734 | 757FEB9D1B3ACF5600B4F327 /* Release */ = { 735 | isa = XCBuildConfiguration; 736 | buildSettings = { 737 | BUNDLE_LOADER = "$(TEST_HOST)"; 738 | INFOPLIST_FILE = "SwiftDaily-ZhiHuTests/Info.plist"; 739 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 740 | PRODUCT_BUNDLE_IDENTIFIER = "com.nickTD.SwiftDaily-ZhiHuTests"; 741 | PRODUCT_NAME = "Swift-ZHITests"; 742 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Swift-ZHI.app/Swift-ZHI"; 743 | }; 744 | name = Release; 745 | }; 746 | /* End XCBuildConfiguration section */ 747 | 748 | /* Begin XCConfigurationList section */ 749 | 757FEB6B1B3ACF5600B4F327 /* Build configuration list for PBXProject "Swift-ZHI" */ = { 750 | isa = XCConfigurationList; 751 | buildConfigurations = ( 752 | 757FEB961B3ACF5600B4F327 /* Debug */, 753 | 757FEB971B3ACF5600B4F327 /* Release */, 754 | ); 755 | defaultConfigurationIsVisible = 0; 756 | defaultConfigurationName = Release; 757 | }; 758 | 757FEB981B3ACF5600B4F327 /* Build configuration list for PBXNativeTarget "Swift-ZHI" */ = { 759 | isa = XCConfigurationList; 760 | buildConfigurations = ( 761 | 757FEB991B3ACF5600B4F327 /* Debug */, 762 | 757FEB9A1B3ACF5600B4F327 /* Release */, 763 | ); 764 | defaultConfigurationIsVisible = 0; 765 | defaultConfigurationName = Release; 766 | }; 767 | 757FEB9B1B3ACF5600B4F327 /* Build configuration list for PBXNativeTarget "Swift-ZHITests" */ = { 768 | isa = XCConfigurationList; 769 | buildConfigurations = ( 770 | 757FEB9C1B3ACF5600B4F327 /* Debug */, 771 | 757FEB9D1B3ACF5600B4F327 /* Release */, 772 | ); 773 | defaultConfigurationIsVisible = 0; 774 | defaultConfigurationName = Release; 775 | }; 776 | /* End XCConfigurationList section */ 777 | }; 778 | rootObject = 757FEB681B3ACF5600B4F327 /* Project object */; 779 | } 780 | -------------------------------------------------------------------------------- /Swift-ZHI.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Swift-ZHI.xcodeproj/xcshareddata/xcschemes/Swift-ZHI.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 16 | 22 | 23 | 24 | 25 | 26 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 67 | 73 | 74 | 75 | 76 | 77 | 78 | 84 | 86 | 92 | 93 | 94 | 95 | 97 | 98 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /Swift-ZHI.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/.gitkeep -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/Controllers/.gitkeep -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Controllers/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftDaily-ZhiHu 4 | // 5 | // Created by Nicholas Tian on 5/29/15 6 | // Copyright (c) 2015 nickTD. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | var window: UIWindow? 14 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 15 | if let splitViewController = window!.rootViewController as? UISplitViewController { 16 | setUpSplitViewController(splitViewController) 17 | } 18 | 19 | UserPreferences.registerDefaults() 20 | 21 | return true 22 | } 23 | } 24 | 25 | extension AppDelegate { 26 | private func setUpSplitViewController(svc: UISplitViewController) { 27 | // TODO: Check whether next two lines is needed. 28 | let navigationController = svc.viewControllers[svc.viewControllers.count-1] as! UINavigationController 29 | navigationController.topViewController!.navigationItem.leftBarButtonItem = svc.displayModeButtonItem() 30 | 31 | svc.preferredDisplayMode = .AllVisible 32 | svc.delegate = self 33 | } 34 | } 35 | 36 | // MARK: - Split View Delegate 37 | extension AppDelegate: UISplitViewControllerDelegate { 38 | func splitViewController(splitViewController: UISplitViewController, collapseSecondaryViewController secondaryViewController:UIViewController, ontoPrimaryViewController primaryViewController:UIViewController) -> Bool { 39 | if let secondaryAsNavController = secondaryViewController as? UINavigationController { 40 | if let topAsDetailController = secondaryAsNavController.topViewController as? RealmNewsViewController { 41 | if topAsDetailController.newsId == nil { 42 | // Without this, RealmNewsViewController is first shown when on iPhone. 43 | return true 44 | } 45 | } 46 | } 47 | 48 | return false 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Controllers/DailyTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DailyTableViewController.swift 3 | // SwiftDaily-ZhiHu 4 | // 5 | // Created by Nicholas Tian on 26/06/2015. 6 | // Copyright © 2015 nickTD. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AMScrollingNavbar 11 | 12 | class DailyTableViewController: HideNavBarViewController { 13 | // MARK: UI 14 | @IBOutlet weak var tableView: UITableView! 15 | let refreshControl: UIRefreshControl = UIRefreshControl() 16 | 17 | var firstAppeared = false 18 | let dateFormatter: NSDateFormatter = { 19 | let dateFormatter = NSDateFormatter() 20 | dateFormatter.dateStyle = NSDateFormatterStyle.MediumStyle 21 | return dateFormatter 22 | }() 23 | } 24 | 25 | // MARK: Abstract methods 26 | extension DailyTableViewController { 27 | func hasDailyAtIndexPath(indexPath: NSIndexPath) -> Bool { 28 | fatalError() 29 | } 30 | 31 | func hasNewsMetaAtIndexPath(indexPath: NSIndexPath) -> Bool { 32 | fatalError() 33 | } 34 | 35 | func hasNewsAtIndexPath(indexPath: NSIndexPath) -> Bool { 36 | return false 37 | } 38 | 39 | func dateStringAtSection(section: Int) -> String { 40 | fatalError() 41 | } 42 | 43 | func loadLatestDaily() { 44 | } 45 | 46 | func loadDailyAtIndexPath(indexPath: NSIndexPath) { 47 | } 48 | 49 | func loadNewsAtIndexPath(indexPath: NSIndexPath) { 50 | } 51 | 52 | func deleteNewsAtIndexPath(indexPath: NSIndexPath) { 53 | } 54 | 55 | // Will only be called if `hasNewsMetaAtIndexPath` returns `true` 56 | func cellAtIndexPath(indexPath: NSIndexPath) -> UITableViewCell { 57 | fatalError() 58 | } 59 | } 60 | 61 | // MARK: UI methods 62 | extension DailyTableViewController { 63 | override func viewDidLoad() { 64 | super.viewDidLoad() 65 | 66 | setupUi() 67 | } 68 | 69 | func setupUi() { 70 | refreshControl.addTarget(self, action: "refreshLatestDaily", forControlEvents: UIControlEvents.ValueChanged) 71 | 72 | tableView.addSubview(refreshControl) 73 | tableView.registerNib(UINib(nibName: "DailySectionHeaderView", bundle: nil), forHeaderFooterViewReuseIdentifier: "DailySectionHeaderView") 74 | } 75 | 76 | 77 | @IBAction func refreshLatestDaily() { 78 | loadLatestDaily() 79 | } 80 | 81 | override func viewWillAppear(animated: Bool) { 82 | super.viewWillAppear(animated) 83 | 84 | if !firstAppeared { 85 | beginRefreshing() 86 | firstAppeared = true 87 | loadLatestDaily() 88 | } 89 | 90 | if let indexPath = tableView.indexPathForSelectedRow { 91 | tableView.deselectRowAtIndexPath(indexPath, animated: true) 92 | } 93 | } 94 | 95 | func beginRefreshing() { 96 | tableView.setContentOffset(CGPoint(x: 0, y: -refreshControl.frame.size.height), animated: true) 97 | refreshControl.beginRefreshing() 98 | } 99 | } 100 | 101 | // MARK: Data Source 102 | extension DailyTableViewController { 103 | func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 104 | if hasNewsMetaAtIndexPath(indexPath) { 105 | return cellAtIndexPath(indexPath) 106 | } else { 107 | let loadingCell = tableView.dequeueReusableCellWithIdentifier("LoadingCell", forIndexPath: indexPath) as! LoadingCell 108 | loadingCell.activityIndicator.startAnimating() 109 | return loadingCell 110 | } 111 | } 112 | 113 | func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool { 114 | return true 115 | } 116 | 117 | func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { 118 | } 119 | } 120 | 121 | // MARK: Delegate 122 | extension DailyTableViewController { 123 | func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { 124 | if let header = tableView.dequeueReusableHeaderFooterViewWithIdentifier("DailySectionHeaderView") as? DailySectionHeaderView { 125 | header.backgroundView = { 126 | let view = UIView(frame: header.bounds) 127 | // HACK: To put color in Storyboard, not in code 128 | view.backgroundColor = header.titleLabel.highlightedTextColor 129 | return view 130 | }() 131 | 132 | header.titleLabel.text = self.dateStringAtSection(section) 133 | 134 | return header 135 | } else { 136 | return nil 137 | } 138 | } 139 | 140 | func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { 141 | if let _ = cell as? LoadingCell where !hasDailyAtIndexPath(indexPath) { 142 | loadDailyAtIndexPath(indexPath) 143 | } 144 | } 145 | 146 | func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? { 147 | if hasNewsAtIndexPath(indexPath) { 148 | return [deleteNewsAction()] 149 | } else { 150 | return [downloadAndSaveNewsAction()] 151 | } 152 | } 153 | 154 | private func downloadAndSaveNewsAction() -> UITableViewRowAction { 155 | let download = NSLocalizedString("Download & Save", 156 | comment: "Download button in DailyView") 157 | let save = UITableViewRowAction(style: .Default, title: download) 158 | { (_, indexPath) in 159 | self.tableView.setEditing(false, animated: true) 160 | self.loadNewsAtIndexPath(indexPath) 161 | } 162 | save.backgroundColor = UIColor(hue: 0.353, saturation: 0.635, brightness: 0.765, alpha: 1) 163 | 164 | return save 165 | } 166 | 167 | private func deleteNewsAction() -> UITableViewRowAction { 168 | let deleteString = NSLocalizedString("Delete", comment: "Delete News button in DailyView") 169 | let deleteAction = UITableViewRowAction(style: .Default, title: deleteString) 170 | { (_, indexPath) in 171 | self.tableView.setEditing(false, animated: true) 172 | 173 | // HACK: To let the animation finish 174 | // before table view gets refreshed 175 | delay(0.3) { 176 | self.deleteNewsAtIndexPath(indexPath) 177 | } 178 | } 179 | 180 | return deleteAction 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Controllers/HideNavBarViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HideNavBarViewController.swift 3 | // SwiftDaily-ZhiHu 4 | // 5 | // Created by Nicholas Tian on 1/07/2015. 6 | // Copyright © 2015 nickTD. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AMScrollingNavbar 11 | 12 | 13 | class HideNavBarViewController: HidesHairLineUnderNavBarViewController { 14 | @IBOutlet weak var scrollableView: UIView! 15 | @IBOutlet weak var scrollableViewTopConstraint: NSLayoutConstraint! 16 | 17 | deinit { 18 | stopFollowingScrollView() 19 | } 20 | } 21 | 22 | // MARK: UI methods 23 | extension HideNavBarViewController { 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | 27 | followScrollView(scrollableView, usingTopConstraint: scrollableViewTopConstraint) 28 | } 29 | 30 | override func viewWillDisappear(animated: Bool) { 31 | showNavBarAnimated(false) 32 | 33 | super.viewWillDisappear(animated) 34 | } 35 | } 36 | 37 | extension HideNavBarViewController: UIScrollViewDelegate { 38 | func scrollViewShouldScrollToTop(scrollView: UIScrollView) -> Bool { 39 | // NOTE: Must set self to scrollView's delegate 40 | showNavbar() 41 | 42 | return true 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Controllers/HidesHairLineUnderNavBarViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HidesHairLineUnderNavBarViewController.swift 3 | // SwiftDaily-ZhiHu 4 | // 5 | // Created by Nicholas Tian on 22/06/2015. 6 | // Copyright © 2015 nickTD. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class HidesHairLineUnderNavBarViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | // source: http://stackoverflow.com/questions/19226965/how-to-hide-ios7-uinavigationbar-1px-bottom-line 17 | 18 | if let bar = navigationController?.navigationBar { 19 | findHairLineImageViewUnder(bar)?.hidden = true 20 | } 21 | 22 | } 23 | 24 | private func findHairLineImageViewUnder(view: UIView) -> UIImageView? { 25 | if view.isKindOfClass(UIImageView.self) && view.bounds.size.height <= 1.0 { 26 | return view as? UIImageView 27 | } 28 | 29 | for subView in view.subviews { 30 | if let imageView = findHairLineImageViewUnder(subView ) { 31 | return imageView 32 | } else { continue } 33 | } 34 | return nil 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Controllers/NewsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NewsViewController.swift 3 | // SwiftDaily-ZhiHu 4 | // 5 | // Created by Nicholas Tian on 27/06/2015. 6 | // Copyright © 2015 nickTD. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AMScrollingNavbar 11 | 12 | class NewsViewController: HideNavBarViewController { 13 | // MARK: UI 14 | @IBOutlet weak var activityIndicator: UIActivityIndicatorView! 15 | @IBOutlet weak var webView: UIWebView! 16 | } 17 | 18 | // MARK: Abstract methods 19 | extension NewsViewController { 20 | func loadNews() { 21 | } 22 | } 23 | 24 | // MARK: UI methods 25 | extension NewsViewController { 26 | override func viewDidLoad() { 27 | super.viewDidLoad() 28 | 29 | webView.scrollView.delegate = self 30 | navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem() 31 | } 32 | 33 | override func viewWillAppear(animated: Bool) { 34 | super.viewWillAppear(animated) 35 | 36 | loadNews() 37 | } 38 | 39 | override func viewWillDisappear(animated: Bool) { 40 | saveReadingProgress() 41 | 42 | super.viewWillDisappear(animated) 43 | } 44 | 45 | func saveReadingProgress() { 46 | let offset = webView.scrollView.contentOffset.y 47 | let height = webView.scrollView.contentSize.height 48 | let percentage = Double(offset/height) 49 | print("height: \(height), offset: \(offset), \(percentage*100)% read") 50 | 51 | // TODO: Actually save the percentage 52 | } 53 | 54 | func stopIndicator() { 55 | activityIndicator.stopAnimating() 56 | activityIndicator.hidden = true 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Controllers/RealmCommentViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RealmCommentViewController.swift 3 | // Swift-ZHI 4 | // 5 | // Created by Nicholas Tian on 27/07/2015. 6 | // Copyright (c) 2015 nickTD. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftDailyAPI 11 | import SDWebImage 12 | import RealmSwift 13 | 14 | class RealmCommentViewController: UIViewController { 15 | var newsId: Int! 16 | 17 | @IBOutlet weak var tableView: UITableView! 18 | let refreshControl = UIRefreshControl() 19 | 20 | private var store = DailyRealmStore() 21 | private var token: NotificationToken? 22 | 23 | private var commentsSortDescriptors = [ 24 | SortDescriptor(property: "isShortComment", ascending: true), 25 | SortDescriptor(property: "repliedAt", ascending: false) 26 | ] 27 | private var comments: Results { 28 | get { 29 | return store.commentsForNewsId(newsId).sorted(self.commentsSortDescriptors) 30 | } 31 | } 32 | 33 | private var dateFormatter: NSDateFormatter = { 34 | let formatter = NSDateFormatter() 35 | formatter.timeStyle = .ShortStyle 36 | formatter.dateStyle = .ShortStyle 37 | return formatter 38 | }() 39 | } 40 | 41 | // MARK: 42 | extension RealmCommentViewController { 43 | func loadNewsComments() { 44 | if let newsId = newsId { // TODO: Swift 2.0, guard 45 | beginRefreshing() 46 | store.shortComments(forNewsId: newsId) 47 | store.longComments(forNewsId: newsId) 48 | } 49 | } 50 | 51 | func commentInSection(section: Int) -> CommentObject { 52 | return comments[section] 53 | } 54 | } 55 | 56 | // MARK: DidScrollToEnd 57 | extension RealmCommentViewController: UIScrollViewDelegate { 58 | func scrollViewDidEndDecelerating(scrollView: UIScrollView) { 59 | let didComeToTheEnd = scrollView.contentOffset.y + scrollView.frame.size.height >= scrollView.contentSize.height 60 | if let lastShortComment = store.shortCommentsForNewsId(newsId).sorted("commentId", ascending: true).first where didComeToTheEnd { 61 | // TODO: There could be a test for whether this is the correct way to get the last comment or not 62 | store.shortComments(forNewsId: newsId, beforeCommentId: lastShortComment.commentId) 63 | } 64 | } 65 | } 66 | 67 | // MARK: UI methods 68 | extension RealmCommentViewController { 69 | override func viewDidLoad() { 70 | super.viewDidLoad() 71 | 72 | setupUi() 73 | setupRealm() 74 | } 75 | 76 | private func setupRealm() { 77 | token = defaultRealm().addNotificationBlock { (_, _) in 78 | self.tableView.reloadData() 79 | self.refreshControl.endRefreshing() 80 | } 81 | } 82 | 83 | private func setupUi() { 84 | tableView.rowHeight = UITableViewAutomaticDimension 85 | tableView.estimatedRowHeight = 44 86 | 87 | refreshControl.addTarget(self, action: "loadNewsComments", forControlEvents: .ValueChanged) 88 | tableView.addSubview(refreshControl) 89 | 90 | tableView.registerNib(UINib(nibName: "CommentSectionHeaderView", bundle: nil), 91 | forHeaderFooterViewReuseIdentifier: "CommentSectionHeaderView") 92 | } 93 | 94 | func beginRefreshing() { 95 | tableView.setContentOffset(CGPoint(x: 0, y: -refreshControl.frame.size.height), animated: true) 96 | refreshControl.beginRefreshing() 97 | } 98 | 99 | override func viewWillAppear(animated: Bool) { 100 | super.viewWillAppear(animated) 101 | 102 | loadNewsComments() 103 | } 104 | } 105 | 106 | // MARK: Data Source 107 | extension RealmCommentViewController: UITableViewDataSource { 108 | func numberOfSectionsInTableView(tableView: UITableView) -> Int { 109 | return comments.count 110 | } 111 | 112 | func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 113 | return 1 114 | } 115 | 116 | func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 117 | let cell = tableView.dequeueReusableCellWithIdentifier("CommentCell/TextOnly")! 118 | 119 | let comment = commentInSection(indexPath.section) 120 | 121 | cell.textLabel?.attributedText = CommentContentFormatter(comment: comment).attributedContent 122 | cell.textLabel?.sizeToFit() 123 | cell.sizeToFit() 124 | 125 | return cell 126 | } 127 | } 128 | 129 | extension RealmCommentViewController: UITableViewDelegate { 130 | func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { 131 | if let header = tableView.dequeueReusableHeaderFooterViewWithIdentifier("CommentSectionHeaderView") as? CommentSectionHeaderView { 132 | // FIX/HACK: cant create a header view with `contentView` like the one in the xib for UITableViewCell 133 | header.backgroundView = { 134 | let view = UIView(frame: header.bounds) 135 | view.backgroundColor = tableView.backgroundColor 136 | view.alpha = 0.96 137 | return view 138 | }() 139 | 140 | let comment = commentInSection(section) 141 | 142 | header.usernameLabel.text = comment.authorName 143 | header.repliedAtLabel.text = dateFormatter.stringFromDate(comment.repliedAt) 144 | 145 | header.longCommentIndicator.hidden = comment.isShortComment 146 | 147 | header.avatarImageView.sd_setImageWithURL(comment.avatarURL) 148 | 149 | return header 150 | } else { 151 | return nil 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Controllers/RealmDailyTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RealmDailyTableViewController.swift 3 | // SwiftDaily-ZhiHu 4 | // 5 | // Created by Nicholas Tian on 24/06/2015. 6 | // Copyright © 2015 nickTD. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RealmSwift 11 | import SwiftDailyAPI 12 | 13 | class RealmDailyTableViewController: DailyTableViewController { 14 | private let store = DailyRealmStore() 15 | private let dailyDates = DailyDates() 16 | 17 | private var token: NotificationToken? 18 | 19 | private func dailyAtDate(date: NSDate) -> DailyObject? { 20 | return store.dailyAtDate(date) 21 | } 22 | 23 | private func newsMetaAtIndexPath(indexPath: NSIndexPath) -> NewsMetaObject? { 24 | let date = dailyDates.dateAtIndex(indexPath.section) 25 | if let daily = dailyAtDate(date) { 26 | return daily.news[indexPath.row] 27 | } else { 28 | return nil 29 | } 30 | } 31 | 32 | private func hasNewsWithId(newsId: Int) -> Bool { 33 | let news = store.newsWithId(newsId) 34 | return news != nil 35 | } 36 | } 37 | 38 | // MARK: Concrete methods 39 | extension RealmDailyTableViewController { 40 | override func hasDailyAtIndexPath(indexPath: NSIndexPath) -> Bool { 41 | let date = dailyDates.dateAtIndex(indexPath.section) 42 | return store.dailyAtDate(date) != nil 43 | } 44 | 45 | override func hasNewsMetaAtIndexPath(indexPath: NSIndexPath) -> Bool { 46 | return newsMetaAtIndexPath(indexPath) != nil 47 | } 48 | 49 | override func hasNewsAtIndexPath(indexPath: NSIndexPath) -> Bool { 50 | if let newsMeta = newsMetaAtIndexPath(indexPath) { 51 | return store.newsWithId(newsMeta.newsId) != nil 52 | } else { 53 | return false 54 | } 55 | } 56 | 57 | override func dateStringAtSection(section: Int) -> String { 58 | let date = dailyDates.dateAtIndex(section) 59 | return dateFormatter.stringFromDate(date) 60 | } 61 | 62 | override func loadDailyAtIndexPath(indexPath: NSIndexPath) { 63 | store.daily(forDate: dailyDates.dateAtIndex(indexPath.section)) 64 | } 65 | 66 | override func loadLatestDaily() { 67 | store.updateLatestDaily() 68 | } 69 | 70 | override func loadNewsAtIndexPath(indexPath: NSIndexPath) { 71 | if let newsMeta = self.newsMetaAtIndexPath(indexPath) { 72 | self.store.news(newsMeta.newsId) 73 | } else { 74 | return 75 | } 76 | } 77 | 78 | override func deleteNewsAtIndexPath(indexPath: NSIndexPath) { 79 | if let newsMeta = newsMetaAtIndexPath(indexPath) { 80 | store.deleteNewsWithId(newsMeta.newsId) 81 | } 82 | } 83 | 84 | override func cellAtIndexPath(indexPath: NSIndexPath) -> UITableViewCell { 85 | let cell = tableView.dequeueReusableCellWithIdentifier("NewsMetaCell", forIndexPath: indexPath) 86 | let newsMeta = newsMetaAtIndexPath(indexPath)! 87 | 88 | cell.textLabel!.text = newsMeta.title 89 | 90 | if hasNewsWithId(newsMeta.newsId) { 91 | cell.accessoryType = .Checkmark 92 | } else { 93 | cell.accessoryType = .None 94 | } 95 | 96 | return cell 97 | } 98 | } 99 | 100 | // MARK: UI methods 101 | extension RealmDailyTableViewController { 102 | override func viewDidLoad() { 103 | super.viewDidLoad() 104 | 105 | token = defaultRealm().addNotificationBlock { (_, _) in 106 | self.dailyDates.endDate = self.store.latestDate 107 | self.reloadTableViewWhenNotEditing() 108 | } 109 | } 110 | 111 | func reloadTableViewWhenNotEditing() { 112 | if tableView.editing { 113 | delay(0.1) { self.reloadTableViewWhenNotEditing() } 114 | } else { 115 | // HACK: To let the animation for ending editing finish 116 | // before table view gets refreshed 117 | delay(0.3) { self.reloadTableView() } 118 | } 119 | } 120 | 121 | func reloadTableView() { 122 | tableView.reloadData() 123 | refreshControl.endRefreshing() 124 | } 125 | 126 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 127 | if segue.identifier == "showNews/Realm" { 128 | if let indexPath = tableView.indexPathForSelectedRow, 129 | let newsMeta = newsMetaAtIndexPath(indexPath), 130 | let nvc = segue.destinationViewController as? UINavigationController, 131 | let vc = nvc.topViewController as? RealmNewsViewController { 132 | vc.newsId = newsMeta.newsId 133 | vc.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem() 134 | vc.navigationItem.leftItemsSupplementBackButton = true 135 | } 136 | } 137 | } 138 | } 139 | 140 | // MARK: Data Source 141 | extension RealmDailyTableViewController: UITableViewDataSource { 142 | func numberOfSectionsInTableView(tableView: UITableView) -> Int { 143 | return dailyDates.days() 144 | } 145 | 146 | func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 147 | let date = dailyDates.dateAtIndex(section) 148 | if let daily = dailyAtDate(date) { 149 | return daily.news.count 150 | } else { 151 | return 1 152 | } 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Controllers/RealmNewsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RealmNewsViewController.swift 3 | // SwiftDaily-ZhiHu 4 | // 5 | // Created by Nicholas Tian on 27/06/2015. 6 | // Copyright © 2015 nickTD. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RealmSwift 11 | 12 | class RealmNewsViewController: NewsViewController { 13 | var newsId: Int? 14 | 15 | private let store = DailyRealmStore() 16 | private let preferences = UserPreferences() 17 | private var loadedNewsId: Int? 18 | 19 | private var token: NotificationToken? 20 | } 21 | 22 | // MARK: UI methods 23 | extension RealmNewsViewController { 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | 27 | token = defaultRealm().addNotificationBlock { (_, _) in 28 | if let newsId = self.newsId, 29 | let news = self.store.newsWithId(newsId) where newsId != self.loadedNewsId { 30 | self.loadNews(news) 31 | self.stopIndicator() 32 | } 33 | } 34 | } 35 | 36 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 37 | if segue.identifier == "showComments/Realm" { 38 | if let commentsVC = segue.destinationViewController as? RealmCommentViewController { 39 | commentsVC.newsId = newsId ?? preferences.lastReadNewsId 40 | } 41 | } 42 | } 43 | } 44 | 45 | // Concrete methods 46 | extension RealmNewsViewController { 47 | override func loadNews() { 48 | let newsId = self.newsId ?? preferences.lastReadNewsId 49 | 50 | if let news = store.newsWithId(newsId) { 51 | loadNews(news) 52 | didLoadNewsInWebViewWithNewsId(newsId) 53 | } else { 54 | activityIndicator.startAnimating() 55 | store.news(newsId) 56 | } 57 | } 58 | 59 | private func didLoadNewsInWebViewWithNewsId(newsId: Int) { 60 | loadedNewsId = newsId 61 | preferences.lastReadNewsId = newsId 62 | } 63 | 64 | // TODO: Use protocol 65 | private func loadNews(news: NewsObject) { 66 | let css = news.cssURLStrings.map { "" } 67 | var newsBody = css.reduce(news.body) { $0 + $1 } 68 | // hide 200px #div.img-place-holder in css 69 | newsBody += "" 70 | 71 | webView.loadHTMLString(newsBody, baseURL: nil) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Extensions/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/Extensions/.gitkeep -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/Helpers/.gitkeep -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Helpers/CommentContentFormatter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CommentContentFormatter.swift 3 | // Swift-ZHI 4 | // 5 | // Created by Nicholas Tian on 31/07/2015. 6 | // Copyright (c) 2015 nickTD. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftDailyAPI 11 | 12 | struct CommentContentFormatter { 13 | let comment: CommentObject 14 | let replyToComment: ReplyToCommentObject? 15 | static let replyString = NSLocalizedString("Reply", comment: "Reply string in CommentView") 16 | static let newlinesBetweenCommentAndReply = "\n\n" 17 | 18 | init(comment: CommentObject) { 19 | self.comment = comment 20 | self.replyToComment = comment.replyToComment 21 | } 22 | 23 | var attributedContent: NSAttributedString { 24 | get { 25 | let attributedContent = NSMutableAttributedString(string: content) 26 | let systemFontSize = UIFont.systemFontSize() 27 | 28 | attributedContent.addAttributes( 29 | [ 30 | NSForegroundColorAttributeName: UIColor.grayColor(), 31 | NSFontAttributeName: UIFont.systemFontOfSize(systemFontSize - 1) 32 | ], 33 | range: rangeOfReplyContentWithNewlines 34 | ) 35 | attributedContent.addAttributes( 36 | [NSFontAttributeName: UIFont.boldSystemFontOfSize(systemFontSize)], 37 | range: rangeOfReplyAuthor 38 | ) 39 | 40 | return attributedContent 41 | } 42 | } 43 | 44 | var rangeOfReplyAuthor: NSRange { 45 | get { return (content as NSString).rangeOfString(replyAuthorString) } 46 | } 47 | 48 | var rangeOfReplyContentWithNewlines: NSRange { 49 | get { return (content as NSString).rangeOfString(replyContentString) } 50 | } 51 | 52 | var content: String { 53 | get { 54 | return replyContentStringWithNewlines + commentContentString 55 | } 56 | } 57 | 58 | var commentContentString: String { get { return comment.content } } 59 | 60 | var replyContentStringWithNewlines: String { 61 | get { 62 | if replyToComment == nil { 63 | return "" 64 | } 65 | 66 | return CommentContentFormatter.replyString + " " + replyContentString + CommentContentFormatter.newlinesBetweenCommentAndReply 67 | } 68 | } 69 | 70 | var replyContentString: String { get { return replyAuthorString + "\n\n" + replyCommentString} } 71 | 72 | var replyCommentString: String { get { return replyToComment?.content ?? "" } } 73 | 74 | var replyAuthorString: String { get { return replyToComment?.authorName ?? "" } } 75 | } 76 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Helpers/DailyDates.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DailyDates.swift 3 | // SwiftDaily-ZhiHu 4 | // 5 | // Created by Nicholas Tian on 26/06/2015. 6 | // Copyright © 2015 nickTD. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class DailyDates { 12 | let startDate = NSDate.dateAt(year: 2013, month: 05, day: 19)! 13 | var endDate: NSDate 14 | 15 | private var calendar = NSCalendar.currentCalendar() 16 | 17 | init(endDate: NSDate = NSDate()) { 18 | self.endDate = endDate 19 | } 20 | 21 | func dateAtIndex(index: Int) -> NSDate { 22 | return calendar.dateByAddingUnit(.Day, value: -index, toDate: endDate, options: [])! 23 | } 24 | 25 | func days() -> Int { 26 | return calendar.components(.Day, fromDate: startDate, toDate: endDate, options: []).day + 1 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Helpers/Delay.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Delay.swift 3 | // Swift-ZHI 4 | // 5 | // Created by Nicholas Tian on 11/07/2015. 6 | // Copyright © 2015 nickTD. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | func delay(seconds: Double, queue: dispatch_queue_t = dispatch_get_main_queue(), closure: () -> Void) { 12 | let delay = dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC))) 13 | dispatch_after(delay, queue, closure) 14 | } 15 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Helpers/RealmHelpers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RealmHelpers.swift 3 | // SwiftDaily-ZhiHu 4 | // 5 | // Created by Nicholas Tian on 25/06/2015. 6 | // Copyright © 2015 nickTD. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RealmSwift 11 | 12 | func defaultRealm() -> Realm { 13 | // TODO: Until next release, use in memroy realm 14 | // NOTE: So there's no need to migrate XD until a stable model 15 | 16 | return try! Realm() 17 | // return Realm(inMemoryIdentifier: "DailyTestGround") 18 | } 19 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/Models/.gitkeep -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Models/DailyRealmStore.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DailyRealmStore.swift 3 | // SwiftDaily-ZhiHu 4 | // 5 | // Created by Nicholas Tian on 25/06/2015. 6 | // Copyright © 2015 nickTD. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RealmSwift 11 | import SwiftDailyAPI 12 | 13 | public class DailyRealmStore { 14 | public var latestDaily: LatestDaily? 15 | public var latestDate: NSDate { 16 | return latestDaily?.date ?? NSDate() 17 | } 18 | 19 | let dailyAPI: DailyAPI 20 | let realm = defaultRealm() 21 | 22 | public init(completionQueue: dispatch_queue_t? = nil) 23 | { 24 | dailyAPI = DailyAPI(completionQueue: completionQueue) 25 | } 26 | } 27 | 28 | extension DailyRealmStore { 29 | // MARK: Insertion by calling to `DailyAPI` 30 | public func updateLatestDaily() { 31 | dailyAPI.latestDaily { latestDaily in 32 | self.latestDaily = latestDaily 33 | self.addDaily(Daily(latestDaily)) 34 | } 35 | } 36 | 37 | public func daily(forDate date: NSDate) { 38 | dailyAPI.daily(forDate: date) { self.addDaily($0) } 39 | } 40 | 41 | public func news(newsId: Int) { 42 | dailyAPI.news(newsId) { self.addObject(NewsObject.from($0)) } 43 | } 44 | 45 | public func longComments(forNewsId newsId: Int) { 46 | dailyAPI.longComments(newsId) { 47 | $0.comments.forEach { 48 | self.addObject(CommentObject.from($0, forNewsId: newsId, isShortComment: false)) 49 | } 50 | } 51 | } 52 | 53 | public func shortComments(forNewsId newsId: Int) { 54 | dailyAPI.shortComments(newsId) { 55 | self.addShortComments(fromComments: $0, forNewsId: newsId) 56 | } 57 | } 58 | 59 | public func shortComments(forNewsId newsId: Int, beforeCommentId commentId: Int) { 60 | dailyAPI.shortComments(newsId, beforeCommentId: commentId) { 61 | self.addShortComments(fromComments: $0, forNewsId: newsId) 62 | } 63 | } 64 | 65 | // MARK: Deletion 66 | public func deleteNewsWithId(newsId: Int, inRealm realm: Realm = defaultRealm()) { 67 | if let news = newsWithId(newsId) { 68 | try! realm.write { realm.delete(news) } 69 | } 70 | } 71 | } 72 | 73 | // MARK: Getters 74 | extension DailyRealmStore { 75 | public func dailyAtDate(date: NSDate) -> DailyObject? { 76 | let results = realm.objects(DailyObject).filter("dateHash == \(date.hash)") 77 | return results.first 78 | } 79 | 80 | public func newsWithId(newsId: Int) -> NewsObject? { 81 | let results = realm.objects(NewsObject).filter("newsId == \(newsId)") 82 | return results.first 83 | } 84 | 85 | public func commentsForNewsId(newsId: Int) -> Results { 86 | let results = realm.objects(CommentObject).filter("newsId == \(newsId)") 87 | return results 88 | } 89 | 90 | public func shortCommentsForNewsId(newsId: Int) -> Results { 91 | let results = realm.objects(CommentObject).filter("newsId == \(newsId) AND isShortComment == TRUE") 92 | return results 93 | } 94 | } 95 | 96 | // MARK: Private methods 97 | extension DailyRealmStore { 98 | private func addShortComments(fromComments comments: Comments, forNewsId newsId: Int) { 99 | comments.comments.forEach { 100 | self.addObject(CommentObject.from($0, forNewsId: newsId, isShortComment: true)) 101 | } 102 | } 103 | 104 | private func addDaily(daily: Daily) { 105 | addObject(DailyObject.from(daily)) 106 | } 107 | 108 | private func addObject(object: Object, toRealm realm: Realm = defaultRealm()) { 109 | try! realm.write { realm.add(object, update: true) } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Models/RealmModels.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RealmModels.swift 3 | // SwiftDaily-ZhiHu 4 | // 5 | // Created by Nicholas Tian on 24/06/2015. 6 | // Copyright © 2015 nickTD. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RealmSwift 11 | import SwiftDailyAPI 12 | 13 | public class NewsMetaObject: Object { 14 | dynamic public var newsId: Int = 0 15 | dynamic public var title: String = "" 16 | 17 | override static public func primaryKey() -> String? { 18 | return "newsId" 19 | } 20 | 21 | convenience public init(newsId: Int, title: String) { 22 | self.init() 23 | self.newsId = newsId 24 | self.title = title 25 | } 26 | 27 | static public func from(newsMeta: NewsMeta) -> NewsMetaObject { 28 | return NewsMetaObject(newsId: newsMeta.newsId, title: newsMeta.title) 29 | } 30 | } 31 | 32 | public class DailyObject: Object { 33 | dynamic public var dateHash: Int = 0 34 | dynamic public var date: NSDate = NSDate() 35 | public var news = List() 36 | 37 | override public static func primaryKey() -> String? { 38 | return "dateHash" 39 | } 40 | 41 | override public static func indexedProperties() -> [String] { 42 | return ["dateHash"] 43 | } 44 | 45 | convenience public init(date: NSDate, news: [NewsMeta]) { 46 | self.init() 47 | self.date = date 48 | self.dateHash = date.hash 49 | self.news.appendContentsOf(news.map { NewsMetaObject.from($0) }) 50 | } 51 | 52 | static public func from(daily: Daily) -> DailyObject { 53 | // Note: Why not use `convenience init(daily: Daily)` 54 | // Because it will cause "abort trap 6". 55 | 56 | // TODO: Wait for response from bug report: 21559246 57 | return DailyObject(date: daily.date, news: daily.news) 58 | } 59 | } 60 | 61 | public class StringObject: Object { 62 | dynamic public var value: String = "" 63 | 64 | convenience public init(stringValue: String) { 65 | self.init() 66 | self.value = stringValue 67 | } 68 | } 69 | 70 | public class NewsObject: Object { 71 | dynamic public var newsId: Int = 0 72 | dynamic public var title: String = "" 73 | dynamic public var body: String = "" 74 | public var cssURLStrings = List() 75 | 76 | override static public func primaryKey() -> String? { 77 | return "newsId" 78 | } 79 | 80 | convenience public init(newsId: Int, title: String, body: String, cssURLStrings: [String]) { 81 | self.init() 82 | self.newsId = newsId 83 | self.title = title 84 | self.body = body 85 | self.cssURLStrings.appendContentsOf(cssURLStrings.map { StringObject(stringValue: $0) }) 86 | } 87 | 88 | static public func from(news: News) -> NewsObject { 89 | return NewsObject(newsId: news.newsId, title: news.title, body: news.body, cssURLStrings: news.cssURLs.map {$0.absoluteString}) 90 | } 91 | } 92 | 93 | public class ReplyToCommentObject: Object { 94 | dynamic public var authorName: String = "" 95 | dynamic public var content: String = "" 96 | dynamic public var _primaryKey: Int = 0 97 | 98 | 99 | override static public func primaryKey() -> String? { 100 | return "_primaryKey" 101 | } 102 | 103 | public func setAuthorName(authorName: String, content: String) { 104 | self.authorName = authorName 105 | self.content = content 106 | 107 | _primaryKey = "\(authorName)\(content)".hash 108 | } 109 | 110 | static public func from(replyToComment: ReplyToComment?) -> Self? { 111 | // TODO: guard 112 | if let replyToComment = replyToComment { 113 | let object = self.init() 114 | object.setAuthorName(replyToComment.authorName, content: replyToComment.content) 115 | return object 116 | } else { 117 | return nil 118 | } 119 | } 120 | } 121 | 122 | public class CommentObject: Object { 123 | // MARK: vars exist only in Realm 124 | dynamic public var newsId: Int = 0 125 | dynamic public var isShortComment: Bool = true 126 | // MARK: vars in JSON 127 | dynamic public var commentId: Int = 0 128 | dynamic public var authorName: String = "" 129 | dynamic public var content: String = "" 130 | dynamic public var likes: Int = 0 131 | dynamic public var repliedAt: NSDate = NSDate() 132 | dynamic public var avatarURLString: String = "" 133 | dynamic public var replyToComment: ReplyToCommentObject? 134 | 135 | public var avatarURL: NSURL { 136 | get { return NSURL(string: avatarURLString)! } 137 | } 138 | 139 | override static public func primaryKey() -> String? { 140 | return "commentId" 141 | } 142 | 143 | public func setCommentId(commentId: Int , authorName: String , content: String , likes: Int , repliedAt: NSDate , avatarURL: NSURL , replyToComment: ReplyToComment?, newsId: Int, isShortComment: Bool) { 144 | self.newsId = newsId 145 | self.isShortComment = isShortComment 146 | 147 | self.commentId = commentId 148 | self.authorName = authorName 149 | self.content = content 150 | self.likes = likes 151 | self.repliedAt = repliedAt 152 | self.avatarURLString = avatarURL.absoluteString 153 | self.replyToComment = ReplyToCommentObject.from(replyToComment) 154 | } 155 | 156 | // Default to short comment. 157 | static public func from(comment: Comment, forNewsId newsId: Int, isShortComment: Bool) -> Self { 158 | let object = self.init() 159 | 160 | object.setCommentId(comment.commentId, authorName: comment.authorName , content: comment.content , likes: comment.likes , repliedAt: comment.repliedAt, avatarURL: comment.avatarURL, replyToComment: comment.replyToComment, newsId: newsId, isShortComment: isShortComment) 161 | 162 | return object 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Models/UserPreferences.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserPreferences.swift 3 | // Swift-ZHI 4 | // 5 | // Created by Nicholas Tian on 14/07/2015. 6 | // Copyright © 2015 nickTD. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class UserPreferences { 12 | let userDefaults: NSUserDefaults 13 | 14 | static let userDefaultsDictionary = [ 15 | PreferenceKeys.LastReadNewsId.rawValue: 4862871 16 | // Title: 高大上的苹果店,装修究竟好在哪儿? 17 | // Web page URL: http://daily.zhihu.com/story/4862871 18 | ] 19 | 20 | init(userDefaults: NSUserDefaults = NSUserDefaults()) { 21 | self.userDefaults = userDefaults 22 | } 23 | 24 | class func registerDefaults() { 25 | NSUserDefaults().registerDefaults(userDefaultsDictionary) 26 | } 27 | 28 | enum PreferenceKeys: String { 29 | case LastReadNewsId = "lastReadNewsId" 30 | } 31 | 32 | var lastReadNewsId: Int { 33 | get { 34 | return userDefaults.integerForKey(PreferenceKeys.LastReadNewsId.rawValue) 35 | } 36 | 37 | set { 38 | userDefaults.setInteger(newValue, forKey: PreferenceKeys.LastReadNewsId.rawValue) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Protocols/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/Protocols/.gitkeep -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/Resources/.gitkeep -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Base.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/Resources/Base.lproj/Localizable.strings -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "29x29", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-29@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "29x29", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-29@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "40x40", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-40@2x.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "40x40", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-40@3x.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "60x60", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-60@2x.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-60@3x.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "29x29", 41 | "idiom" : "ipad", 42 | "filename" : "Icon-29.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "29x29", 47 | "idiom" : "ipad", 48 | "filename" : "Icon-29@2x-1.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "40x40", 53 | "idiom" : "ipad", 54 | "filename" : "Icon-40.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "40x40", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-40@2x-1.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "76x76", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-76.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "76x76", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-76@2x.png", 73 | "scale" : "2x" 74 | } 75 | ], 76 | "info" : { 77 | "version" : 1, 78 | "author" : "xcode" 79 | } 80 | } -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-29.png -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-29@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-29@2x-1.png -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-29@2x.png -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-29@3x.png -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-40.png -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-40@2x-1.png -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-40@3x.png -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-76.png -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/Resources/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en_US 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.4.8 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.8.10 25 | LSHasLocalizedDisplayName 26 | 27 | LSRequiresIPhoneOS 28 | 29 | NSAppTransportSecurity 30 | 31 | NSExceptionDomains 32 | 33 | zhihu.com 34 | 35 | NSIncludesSubdomains 36 | 37 | NSTemporaryExceptionAllowsInsecureHTTPLoads 38 | 39 | 40 | zhimg.com 41 | 42 | NSIncludesSubdomains 43 | 44 | NSTemporaryExceptionAllowsInsecureHTTPLoads 45 | 46 | 47 | 48 | 49 | UILaunchStoryboardName 50 | LaunchScreen 51 | UIMainStoryboardFile 52 | Main 53 | UIRequiredDeviceCapabilities 54 | 55 | armv7 56 | 57 | UIStatusBarStyle 58 | UIStatusBarStyleLightContent 59 | UISupportedInterfaceOrientations 60 | 61 | UIInterfaceOrientationPortrait 62 | UIInterfaceOrientationLandscapeLeft 63 | UIInterfaceOrientationLandscapeRight 64 | 65 | UIViewControllerBasedStatusBarAppearance 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Localizations/Swift-ZHI/zh-Hans.xliff: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 |
7 | 8 | 9 | ${PRODUCT_NAME} 10 | 速知 11 | 12 | 13 | ${PRODUCT_NAME} 14 | 速知 15 | 16 | 17 | 1.3.5 18 | 19 | 20 |
21 | 22 |
23 | 24 |
25 | 26 | 27 | Delete 28 | 删除 29 | Delete News button in DailyView 30 | 31 | 32 | Download & Save 33 | 下载并保存 34 | Download button in DailyView 35 | 36 | 37 | Reply 38 | 回复 39 | Reply string in CommentView 40 | 41 | 42 |
43 | 44 |
45 | 46 |
47 | 48 | 49 | Title 50 | Class = "UILabel"; text = "Title"; ObjectID = "Gt5-Zn-vsE"; 51 | 52 | 53 | Swift ZHI 54 | 速知 55 | Class = "UIBarButtonItem"; title = "Swift ZHI"; ObjectID = "Sz8-Ml-p5c"; 56 | 57 | 58 | Title 59 | Class = "UILabel"; text = "Title"; ObjectID = "loV-SC-E0Y"; 60 | 61 | 62 | Comments 63 | 评论 64 | Class = "UIBarButtonItem"; title = "Comments"; ObjectID = "qMw-ph-xNh"; 65 | 66 | 67 | Swift ZHI 68 | 速知 69 | Class = "UINavigationItem"; title = "Swift ZHI"; ObjectID = "u84-IW-9ED"; 70 | 71 | 72 |
73 | 74 |
75 | 76 |
77 | 78 | 79 | $(PRODUCT_NAME) 80 | 速知 81 | 82 | 83 | 1.0 84 | 1.0 85 | 86 | 87 |
88 | 89 |
90 | 91 |
92 | 93 | 94 | $(PRODUCT_NAME) 95 | 速知 96 | 97 | 98 | 1.0 99 | 1.0 100 | 101 | 102 |
103 |
104 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Localizations/Swift-ZHI/zh-Hant.xliff: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 |
7 | 8 | 9 | ${PRODUCT_NAME} 10 | 速知 11 | 12 | 13 | ${PRODUCT_NAME} 14 | 速知 15 | 16 | 17 | 1.3.5 18 | 19 | 20 |
21 | 22 |
23 | 24 |
25 | 26 | 27 | Delete 28 | 刪除 29 | Delete News button in DailyView 30 | 31 | 32 | Download & Save 33 | 下載並保存 34 | Download button in DailyView 35 | 36 | 37 | Reply 38 | 回復 39 | Reply string in CommentView 40 | 41 | 42 |
43 | 44 |
45 | 46 |
47 | 48 | 49 | Title 50 | Class = "UILabel"; text = "Title"; ObjectID = "Gt5-Zn-vsE"; 51 | 52 | 53 | Swift ZHI 54 | 速知 55 | Class = "UIBarButtonItem"; title = "Swift ZHI"; ObjectID = "Sz8-Ml-p5c"; 56 | 57 | 58 | Title 59 | Class = "UILabel"; text = "Title"; ObjectID = "loV-SC-E0Y"; 60 | 61 | 62 | Comments 63 | 評論 64 | Class = "UIBarButtonItem"; title = "Comments"; ObjectID = "qMw-ph-xNh"; 65 | 66 | 67 | Swift ZHI 68 | 速知 69 | Class = "UINavigationItem"; title = "Swift ZHI"; ObjectID = "u84-IW-9ED"; 70 | 71 | 72 |
73 | 74 |
75 | 76 |
77 | 78 | 79 | $(PRODUCT_NAME) 80 | 速知 81 | 82 | 83 | 1.0 84 | 1.0 85 | 86 | 87 |
88 | 89 |
90 | 91 |
92 | 93 | 94 | $(PRODUCT_NAME) 95 | 速知 96 | 97 | 98 | 1.0 99 | 1.0 100 | 101 | 102 |
103 |
104 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Nibs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/Resources/Nibs/.gitkeep -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Nibs/Base.lproj/CommentSectionHeaderView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 34 | 41 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Nibs/DailySectionHeaderView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Nibs/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Nibs/zh-Hans.lproj/CommentSectionHeaderView.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "UILabel"; text = "Label"; ObjectID = "Lac-36-zzP"; */ 3 | "Lac-36-zzP.text" = "Label"; 4 | 5 | /* Class = "UILabel"; text = "Long comment"; ObjectID = "gXd-Hz-Wc1"; */ 6 | "gXd-Hz-Wc1.text" = "长评论"; 7 | 8 | /* Class = "UILabel"; text = "Label"; ObjectID = "qD9-Ly-Dbj"; */ 9 | "qD9-Ly-Dbj.text" = "Label"; 10 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Nibs/zh-Hant.lproj/CommentSectionHeaderView.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "UILabel"; text = "Label"; ObjectID = "Lac-36-zzP"; */ 3 | "Lac-36-zzP.text" = "Label"; 4 | 5 | /* Class = "UILabel"; text = "Long comment"; ObjectID = "gXd-Hz-Wc1"; */ 6 | "gXd-Hz-Wc1.text" = "長評論"; 7 | 8 | /* Class = "UILabel"; text = "Label"; ObjectID = "qD9-Ly-Dbj"; */ 9 | "qD9-Ly-Dbj.text" = "Label"; 10 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Settings.bundle/Root.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | 9 | StringsTable 10 | Root 11 | 12 | 13 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Settings.bundle/en.lproj/Root.strings: -------------------------------------------------------------------------------- 1 | /* A single strings file, whose title is specified in your preferences schema. The strings files provide the localized content to display to the user for each of your preferences. */ 2 | 3 | "Acknowledgements" = "Acknowledgements"; 4 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Storyboards/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/Resources/Storyboards/.gitkeep -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Storyboards/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Storyboards/zh-Hans.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | /* Class = "UIBarButtonItem"; title = "Swift ZHI"; ObjectID = "Sz8-Ml-p5c"; */ 2 | "Sz8-Ml-p5c.title" = "速知"; 3 | 4 | /* Class = "UIBarButtonItem"; title = "Comments"; ObjectID = "qMw-ph-xNh"; */ 5 | "qMw-ph-xNh.title" = "评论"; 6 | 7 | /* Class = "UINavigationItem"; title = "Swift ZHI"; ObjectID = "u84-IW-9ED"; */ 8 | "u84-IW-9ED.title" = "速知"; 9 | 10 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/Storyboards/zh-Hant.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | /* Class = "UIBarButtonItem"; title = "Swift ZHI"; ObjectID = "Sz8-Ml-p5c"; */ 2 | "Sz8-Ml-p5c.title" = "速知"; 3 | 4 | /* Class = "UIBarButtonItem"; title = "Comments"; ObjectID = "qMw-ph-xNh"; */ 5 | "qMw-ph-xNh.title" = "評論"; 6 | 7 | /* Class = "UINavigationItem"; title = "Swift ZHI"; ObjectID = "u84-IW-9ED"; */ 8 | "u84-IW-9ED.title" = "速知"; 9 | 10 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/zh-Hans.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* (No Commment) */ 2 | "CFBundleDisplayName" = "速知"; 3 | 4 | /* (No Commment) */ 5 | "CFBundleName" = "速知"; 6 | 7 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/zh-Hans.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* Delete News button in DailyView */ 2 | "Delete" = "删除"; 3 | 4 | /* Download button in DailyView */ 5 | "Download & Save" = "下载并保存"; 6 | 7 | /* Reply string in CommentView */ 8 | "Reply" = "回复"; 9 | 10 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/zh-Hant.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* (No Commment) */ 2 | "CFBundleDisplayName" = "速知"; 3 | 4 | /* (No Commment) */ 5 | "CFBundleName" = "速知"; 6 | 7 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Resources/zh-Hant.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* Delete News button in DailyView */ 2 | "Delete" = "刪除"; 3 | 4 | /* Download button in DailyView */ 5 | "Download & Save" = "下載並保存"; 6 | 7 | /* Reply string in CommentView */ 8 | "Reply" = "回復"; 9 | 10 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/ViewModels/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/ViewModels/.gitkeep -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Views/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/SwiftDaily-ZhiHu/Views/.gitkeep -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Views/CommentSectionHeaderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CommentSectionHeaderView.swift 3 | // Swift-ZHI 4 | // 5 | // Created by Nicholas Tian on 30/07/2015. 6 | // Copyright (c) 2015 nickTD. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class CommentSectionHeaderView: UITableViewHeaderFooterView { 12 | @IBOutlet weak var avatarImageView: UIImageView! 13 | @IBOutlet weak var usernameLabel: UILabel! 14 | @IBOutlet weak var repliedAtLabel: UILabel! 15 | @IBOutlet weak var longCommentIndicator: UILabel! 16 | 17 | var avatarURL: NSURL? 18 | } 19 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Views/DailySectionHeaderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DailySectionHeaderView.swift 3 | // SwiftDaily-ZhiHu 4 | // 5 | // Created by Nicholas Tian on 22/06/2015. 6 | // Copyright © 2015 nickTD. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DailySectionHeaderView: UITableViewHeaderFooterView { 12 | @IBOutlet weak var titleLabel: UILabel! 13 | } 14 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHu/Views/LoadingCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoadingCell.swift 3 | // SwiftDaily-ZhiHu 4 | // 5 | // Created by Nicholas Tian on 17/06/2015. 6 | // Copyright © 2015 nickTD. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class LoadingCell: UITableViewCell { 12 | @IBOutlet weak var activityIndicator: UIActivityIndicatorView! 13 | } 14 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHuTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHuTests/SwiftDaily_ZhiHuTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftDaily_ZhiHuTests.swift 3 | // SwiftDaily-ZhiHuTests 4 | // 5 | // Created by Nicholas Tian on 24/06/2015. 6 | // Copyright © 2015 nickTD. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class SwiftDaily_ZhiHuTests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | super.tearDown() 21 | } 22 | 23 | func testExample() { 24 | // This is an example of a functional test case. 25 | // Use XCTAssert and related functions to verify your tests produce the correct results. 26 | } 27 | 28 | func testPerformanceExample() { 29 | // This is an example of a performance test case. 30 | self.measureBlock() { 31 | // Put the code you want to measure the time of here. 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHuTests/zh-Hans.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* (No Commment) */ 2 | "CFBundleName" = "速知"; 3 | 4 | /* (No Commment) */ 5 | "CFBundleShortVersionString" = "1.0"; 6 | 7 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHuTests/zh-Hant.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* (No Commment) */ 2 | "CFBundleName" = "速知"; 3 | 4 | /* (No Commment) */ 5 | "CFBundleShortVersionString" = "1.0"; 6 | 7 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHuUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHuUITests/SwiftDaily_ZhiHuUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftDaily_ZhiHuUITests.swift 3 | // SwiftDaily-ZhiHuUITests 4 | // 5 | // Created by Nicholas Tian on 24/06/2015. 6 | // Copyright © 2015 nickTD. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | 12 | @available(iOS 9.0, *) 13 | class SwiftDaily_ZhiHuUITests: XCTestCase { 14 | override func setUp() { 15 | super.setUp() 16 | 17 | // Put setup code here. This method is called before the invocation of each test method in the class. 18 | 19 | // In UI tests it is usually best to stop immediately when a failure occurs. 20 | continueAfterFailure = false 21 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 22 | XCUIApplication().launch() 23 | } 24 | 25 | override func tearDown() { 26 | // Put teardown code here. This method is called after the invocation of each test method in the class. 27 | super.tearDown() 28 | } 29 | 30 | func testExample() { 31 | // Use recording to get started writing UI tests. 32 | // Use XCTAssert and related functions to verify your tests produce the correct results. 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHuUITests/zh-Hans.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* (No Commment) */ 2 | "CFBundleName" = "速知"; 3 | 4 | /* (No Commment) */ 5 | "CFBundleShortVersionString" = "1.0"; 6 | 7 | -------------------------------------------------------------------------------- /SwiftDaily-ZhiHuUITests/zh-Hant.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* (No Commment) */ 2 | "CFBundleName" = "速知"; 3 | 4 | /* (No Commment) */ 5 | "CFBundleShortVersionString" = "1.0"; 6 | 7 | -------------------------------------------------------------------------------- /Tests/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/Tests/.gitkeep -------------------------------------------------------------------------------- /Tests/Helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/Tests/Helpers/.gitkeep -------------------------------------------------------------------------------- /Tests/Resources/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/Tests/Resources/.gitkeep -------------------------------------------------------------------------------- /Tests/Resources/Tests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.nickTD.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/Tests/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicholasTD07/Swift-ZHI/2a380d7855f2b55d1973ee59fbf2e5ca4c4b001f/Tests/Tests/.gitkeep -------------------------------------------------------------------------------- /bin/bump: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | SHORT_VERSION=`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" ./SwiftDaily-ZhiHu/Resources/Info.plist` 4 | VERSION=`/usr/libexec/PlistBuddy -c "Print CFBundleVersion" ./SwiftDaily-ZhiHu/Resources/Info.plist` 5 | 6 | git commit -v -e -m "Bump version number $SHORT_VERSION/$VERSION" 7 | -------------------------------------------------------------------------------- /bin/merge: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Merge but keep files in Carthage/Checkouts/* as is in this branch 3 | 4 | git merge --no-commit $* 5 | git checkout ./Carthage/Checkouts/* 6 | echo "Remember to commit the changes. :]" 7 | -------------------------------------------------------------------------------- /bin/new_branch: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Create branches whose base if the merge base of develop and release branch. 3 | # To minimize changes to git's commit graph, when merge new changes into both branch. 4 | 5 | git fetch --all 6 | git checkout -b $* `git merge-base remotes/origin/develop remotes/origin/release/Xcode-6.3.2-Swift-1.2` 7 | -------------------------------------------------------------------------------- /bin/test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o pipefail 4 | 5 | xcodebuild test -workspace SwiftDaily-ZhiHu.xcworkspace -scheme SwiftDaily-ZhiHu -sdk iphonesimulator BUILD_ACTIVE_ARCH=NO | xcpretty -t -c 6 | --------------------------------------------------------------------------------