├── .gitattributes
├── hackfoldr-iOSTests
├── en.lproj
│ └── InfoPlist.strings
├── hackfoldr-iOSTests-Info.plist
├── TestData
│ └── sample.csv.json
└── hackfoldr_iOSTests.m
├── hackfoldr-iOS
├── Images.xcassets
│ ├── Contents.json
│ ├── AppIcon.appiconset
│ │ ├── Icon-76.png
│ │ ├── Icon-60@2x.png
│ │ ├── Icon-60@3x.png
│ │ ├── Icon-76@2x.png
│ │ ├── Icon-Small.png
│ │ ├── Icon-Small-40.png
│ │ ├── Icon-Small@2x.png
│ │ ├── Icon-Small@3x.png
│ │ ├── icon-83.5@2x.png
│ │ ├── Icon-Small-40@2x.png
│ │ ├── Icon-Small-40@3x.png
│ │ ├── Icon-Small@2x-1.png
│ │ ├── iTunesArtwork@2x.png
│ │ ├── Icon-Small-40@2x-1.png
│ │ └── Contents.json
│ ├── CenterIcon.imageset
│ │ ├── CenterIcon.pdf
│ │ └── Contents.json
│ ├── hackfoldr-icon.imageset
│ │ ├── hackfoldr-icon.pdf
│ │ └── Contents.json
│ └── hackfoldr-icon-small.imageset
│ │ ├── hackfoldr-icon-small.pdf
│ │ └── Contents.json
├── en.lproj
│ ├── Hackfoldr.strings
│ └── InfoPlist.strings
├── zh-Hant.lproj
│ ├── InfoPlist.strings
│ └── Hackfoldr.strings
├── UIColor+Hackfoldr.h
├── MainViewController.h
├── hackfoldr-iOS.entitlements
├── HackfoldrClient+Store.h
├── HackfoldrHistory.m
├── main.m
├── TOWebViewController+HackfoldrField.h
├── QRCodeViewController.h
├── hackfoldr-iOS-Prefix.pch
├── NSURL+Hackfoldr.h
├── HackfoldrPage+CSSearchableItem.h
├── SettingViewController.h
├── UIColor+Hackfoldr.m
├── TOWebViewController+HackfoldrField.m
├── HackfoldrHistory.h
├── NSUserDefaults+DefaultHackfoldrPage.h
├── HackfoldrField.h
├── HackfoldrPage.h
├── ListFieldViewController.h
├── HackfoldrClient.h
├── HackoldrModel.xcdatamodeld
│ └── HackoldrModel.xcdatamodel
│ │ └── contents
├── HackfoldrPage+CSSearchableItem.m
├── AppDelegate.h
├── g0v.svg
├── NSUserDefaults+DefaultHackfoldrPage.m
├── NSURL+Hackfoldr.m
├── HackfoldrClient+Store.m
├── hackfoldr-iOS-Info.plist
├── LaunchScreen.storyboard
├── HackfoldrPage.m
├── HackfoldrClient.m
├── AppDelegate.m
├── QRCodeViewController.m
├── QRCode.storyboard
├── MainViewController.m
├── HackfoldrField.m
├── SettingViewController.m
└── ListFieldViewController.m
├── hackfoldr-iOS.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
├── xcshareddata
│ └── xcschemes
│ │ └── hackfoldr-iOS.xcscheme
└── project.pbxproj
├── hackfoldr-iOS.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ └── WorkspaceSettings.xcsettings
├── .gitignore
├── Podfile
├── Readme.md
├── .travis.yml
├── LICENSE
├── script
└── xcode-git-version.sh
├── Changelog.md
└── Podfile.lock
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.pbxproj merge=mergepbx
--------------------------------------------------------------------------------
/hackfoldr-iOSTests/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/hackfoldr-iOS/en.lproj/Hackfoldr.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackfoldr/hackfoldr-iOS/HEAD/hackfoldr-iOS/en.lproj/Hackfoldr.strings
--------------------------------------------------------------------------------
/hackfoldr-iOS/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 | "NSCameraUsageDescription" = "Scan QR code";
4 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/zh-Hant.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 | "NSCameraUsageDescription" = "掃描二維條碼";
4 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/zh-Hant.lproj/Hackfoldr.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackfoldr/hackfoldr-iOS/HEAD/hackfoldr-iOS/zh-Hant.lproj/Hackfoldr.strings
--------------------------------------------------------------------------------
/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackfoldr/hackfoldr-iOS/HEAD/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-76.png
--------------------------------------------------------------------------------
/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackfoldr/hackfoldr-iOS/HEAD/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png
--------------------------------------------------------------------------------
/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackfoldr/hackfoldr-iOS/HEAD/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png
--------------------------------------------------------------------------------
/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackfoldr/hackfoldr-iOS/HEAD/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png
--------------------------------------------------------------------------------
/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-Small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackfoldr/hackfoldr-iOS/HEAD/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-Small.png
--------------------------------------------------------------------------------
/hackfoldr-iOS/Images.xcassets/CenterIcon.imageset/CenterIcon.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackfoldr/hackfoldr-iOS/HEAD/hackfoldr-iOS/Images.xcassets/CenterIcon.imageset/CenterIcon.pdf
--------------------------------------------------------------------------------
/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-Small-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackfoldr/hackfoldr-iOS/HEAD/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-Small-40.png
--------------------------------------------------------------------------------
/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackfoldr/hackfoldr-iOS/HEAD/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png
--------------------------------------------------------------------------------
/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackfoldr/hackfoldr-iOS/HEAD/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png
--------------------------------------------------------------------------------
/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/icon-83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackfoldr/hackfoldr-iOS/HEAD/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/icon-83.5@2x.png
--------------------------------------------------------------------------------
/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackfoldr/hackfoldr-iOS/HEAD/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png
--------------------------------------------------------------------------------
/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackfoldr/hackfoldr-iOS/HEAD/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png
--------------------------------------------------------------------------------
/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackfoldr/hackfoldr-iOS/HEAD/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png
--------------------------------------------------------------------------------
/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/iTunesArtwork@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackfoldr/hackfoldr-iOS/HEAD/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/iTunesArtwork@2x.png
--------------------------------------------------------------------------------
/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-Small-40@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackfoldr/hackfoldr-iOS/HEAD/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Icon-Small-40@2x-1.png
--------------------------------------------------------------------------------
/hackfoldr-iOS/Images.xcassets/hackfoldr-icon.imageset/hackfoldr-icon.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackfoldr/hackfoldr-iOS/HEAD/hackfoldr-iOS/Images.xcassets/hackfoldr-icon.imageset/hackfoldr-icon.pdf
--------------------------------------------------------------------------------
/hackfoldr-iOS/Images.xcassets/hackfoldr-icon-small.imageset/hackfoldr-icon-small.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackfoldr/hackfoldr-iOS/HEAD/hackfoldr-iOS/Images.xcassets/hackfoldr-icon-small.imageset/hackfoldr-icon-small.pdf
--------------------------------------------------------------------------------
/hackfoldr-iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/Images.xcassets/hackfoldr-icon.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "hackfoldr-icon.pdf"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/Images.xcassets/hackfoldr-icon-small.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "hackfoldr-icon-small.pdf"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/hackfoldr-iOS.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/hackfoldr-iOS.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/UIColor+Hackfoldr.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor+Hackfoldr.h
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2017/3/16.
6 | // Copyright © 2017年 org.g0v. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface UIColor (Hackfoldr)
12 |
13 | + (UIColor *)hackfoldrGreenColor;
14 | + (UIColor *)hackfoldrIconWhite;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/MainViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // MainViewController.h
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2014/6/21.
6 | // Copyright (c) 2014年 org.superbil. All rights reserved.
7 | //
8 |
9 | @class HackfoldrField;
10 |
11 | @interface MainViewController : UIViewController
12 |
13 | - (void)updateHackfoldrPageWithKey:(NSString *)hackfoldrKey;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/hackfoldr-iOS.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.developer.associated-domains
6 |
7 | applinks:hackfoldr.org
8 | applinks:*.hackfoldr.org
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/HackfoldrClient+Store.h:
--------------------------------------------------------------------------------
1 | //
2 | // HackfoldrClient+Store.h
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2017/3/17.
6 | // Copyright © 2017年 org.g0v. All rights reserved.
7 | //
8 |
9 | #import "HackfoldrClient.h"
10 |
11 | @interface HackfoldrClient (Store)
12 |
13 | - (HackfoldrTaskCompletionSource *)hackfoldrPageTaskWithKey:(NSString *)hackfoldrKey rediredKey:(NSString *)rediredKey;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/HackfoldrHistory.m:
--------------------------------------------------------------------------------
1 | //
2 | // HackfoldrHistory.m
3 | // hackfoldr-iOS
4 | //
5 | // Created by 舒特比 on 2015/4/3.
6 | // Copyright (c) 2015年 org.superbil. All rights reserved.
7 | //
8 |
9 | #import "HackfoldrHistory.h"
10 |
11 |
12 | @implementation HackfoldrHistory
13 |
14 | @dynamic createDate;
15 | @dynamic hackfoldrKey;
16 | @dynamic rediredKey;
17 | @dynamic refreshDate;
18 | @dynamic title;
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2014/6/21.
6 | // Copyright (c) 2014年 org.superbil. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #import "AppDelegate.h"
12 |
13 | int main(int argc, char * argv[])
14 | {
15 | @autoreleasepool {
16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/TOWebViewController+HackfoldrField.h:
--------------------------------------------------------------------------------
1 | //
2 | // TOWebViewController+HackfoldrField.h
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2014/6/25.
6 | // Copyright (c) 2014年 org.superbil. All rights reserved.
7 | //
8 |
9 | #import "TOWebViewController.h"
10 |
11 | @class HackfoldrField;
12 |
13 | @interface TOWebViewController (HackfoldrField)
14 |
15 | - (void)loadWithField:(HackfoldrField *)oneField;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/Images.xcassets/CenterIcon.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "CenterIcon.pdf",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/hackfoldr-iOS/QRCodeViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // QRCodeViewController.h
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2017/11/22.
6 | // Copyright © 2017年 org.g0v. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface QRCodeViewController : UIViewController
12 |
13 | + (instancetype)viewController;
14 |
15 | @property (copy) NSString *qrCodeString;
16 |
17 | @property (copy) void (^foundedResult)(NSString *result);
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/hackfoldr-iOS-Prefix.pch:
--------------------------------------------------------------------------------
1 | //
2 | // Prefix header
3 | //
4 | // The contents of this file are implicitly included at the beginning of every source file.
5 | //
6 |
7 | #import
8 |
9 | #ifndef __IPHONE_5_0
10 | #warning "This project uses features only available in iOS SDK 5.0 and later."
11 | #endif
12 |
13 | #ifdef __OBJC__
14 | #import
15 | #import
16 | #endif
17 |
18 | #ifndef DEBUG
19 | #define NSLog(...) {}
20 | #endif
--------------------------------------------------------------------------------
/hackfoldr-iOS/NSURL+Hackfoldr.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSURL+Hackfoldr.h
3 | // hackfoldr-iOS
4 | //
5 | // Created by 舒特比 on 2016/12/17.
6 | // Copyright © 2016年 org.g0v. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface NSURL (Hackfoldr)
12 |
13 | + (BOOL)canHandleHackfoldrURL:(nonnull NSURL *)url;
14 |
15 | + (nullable NSString *)realKeyOfHackfoldrWithURL:(nonnull NSURL *)url;
16 |
17 | + (nonnull NSString *)validatorHackfoldrKey:(nonnull NSString *)newHackfoldrKey;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/HackfoldrPage+CSSearchableItem.h:
--------------------------------------------------------------------------------
1 | //
2 | // HackfoldrPage+CSSearchableItem.h
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2017/11/2.
6 | // Copyright © 2017年 org.g0v. All rights reserved.
7 | //
8 |
9 | #import "HackfoldrPage.h"
10 | #import
11 |
12 | @interface HackfoldrPage (CSSearchableItem)
13 |
14 | @property (nonatomic, readonly) CSSearchableItemAttributeSet *searchableAttributeSet;
15 |
16 | - (CSSearchableItem *)searchableItemWithDomain:(NSString *)domain;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/SettingViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // SettingViewController.h
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2017/11/2.
6 | // Copyright © 2017年 org.g0v. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #import
12 |
13 | NS_ASSUME_NONNULL_BEGIN
14 |
15 | @interface SettingViewController : XLFormViewController
16 |
17 | @property (nonatomic, copy, nullable) void (^updateHackfoldrPage)(NSString *pageKey, NSError * _Nullable error);
18 |
19 | @end
20 |
21 | NS_ASSUME_NONNULL_END
22 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/UIColor+Hackfoldr.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor+Hackfoldr.m
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2017/3/16.
6 | // Copyright © 2017年 org.g0v. All rights reserved.
7 | //
8 |
9 | #import "UIColor+Hackfoldr.h"
10 |
11 | @implementation UIColor (Hackfoldr)
12 |
13 | + (UIColor *)hackfoldrGreenColor
14 | {
15 | return [UIColor colorWithRed:0.490 green:0.781 blue:0.225 alpha:1.000];
16 | }
17 |
18 | + (UIColor *)hackfoldrIconWhite {
19 | return [UIColor colorWithRed:0.888 green:0.953 blue:0.826 alpha:1.000];
20 | }
21 |
22 | @end
23 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/TOWebViewController+HackfoldrField.m:
--------------------------------------------------------------------------------
1 | //
2 | // TOWebViewController+HackfoldrField.m
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2014/6/25.
6 | // Copyright (c) 2014年 org.superbil. All rights reserved.
7 | //
8 |
9 | #import "TOWebViewController+HackfoldrField.h"
10 |
11 | #import "HackfoldrField.h"
12 |
13 | @implementation TOWebViewController (HackfoldrField)
14 |
15 | - (void)loadWithField:(HackfoldrField *)oneField
16 | {
17 | self.title = oneField.name;
18 | self.url = [NSURL URLWithString:oneField.urlString];
19 | }
20 |
21 | @end
22 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/HackfoldrHistory.h:
--------------------------------------------------------------------------------
1 | //
2 | // HackfoldrHistory.h
3 | // hackfoldr-iOS
4 | //
5 | // Created by 舒特比 on 2015/4/3.
6 | // Copyright (c) 2015年 org.superbil. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 |
13 | @interface HackfoldrHistory : NSManagedObject
14 |
15 | @property (nonatomic, strong) NSDate * createDate;
16 | @property (nonatomic, strong) NSDate * refreshDate;
17 | @property (nonatomic, strong) NSString * hackfoldrKey;
18 | @property (nonatomic, strong) NSString * rediredKey;
19 | @property (nonatomic, strong) NSString * title;
20 |
21 | @end
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # MacOS
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 |
24 | # CocoaPods
25 | #
26 | # We recommend against adding the Pods directory to your .gitignore. However
27 | # you should judge for yourself, the pros and cons are mentioned at:
28 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
29 | #
30 | Pods/
31 |
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | source 'https://github.com/CocoaPods/Specs.git'
2 |
3 | platform :ios, "9.0"
4 |
5 | target "hackfoldr-iOS" do
6 | pod 'AFNetworking', '~> 3.1'
7 | pod 'AFNetworking/UIKit'
8 | pod 'Bolts', '~> 1.0'
9 | pod 'AFCSVParserResponseSerializer'
10 | pod 'MagicalRecord', '~> 2.2'
11 |
12 | pod 'XLForm', '~> 4.0.0'
13 | pod 'TOWebViewController'
14 | pod 'RATreeView'
15 |
16 | pod 'FontAwesomeKit', :git => 'https://github.com/PrideChung/FontAwesomeKit.git'
17 | pod 'FontAwesomeKit/FontAwesome', :git => 'https://github.com/PrideChung/FontAwesomeKit.git'
18 | pod 'PocketSVG'
19 | end
20 |
21 | target "hackfoldr-iOSTests" do
22 | pod 'OHHTTPStubs'
23 | end
24 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/hackfoldr/hackfoldr-iOS)
2 |
3 | # hackfoldr-iOS #
4 |
5 | This is a simple iOS client for [hackfoldr](https://github.com/hackfoldr/hackfoldr)
6 |
7 | ## Why? ##
8 |
9 | Hackfoldr on mobile is simply not good enough on iOS.
10 |
11 | ## Use CocoaPods developer ##
12 |
13 | [CocoaPods](http://cocoapods.org/) is the easy way to install third-party iOS components.
14 |
15 | ````
16 | pod install
17 | ````
18 |
19 | ## Use Git-flow for teamwork ##
20 |
21 | [Git-Flow](http://nvie.com/posts/a-successful-git-branching-model/)
22 |
23 | ## License ##
24 |
25 | And of course:
26 |
27 | MIT: http://superbil.mit-license.org/
28 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/NSUserDefaults+DefaultHackfoldrPage.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSUserDefaults+DefaultHackfoldrPage.h
3 | // hackfoldr-iOS
4 | //
5 | // Created by 舒特比 on 2015/1/28.
6 | // Copyright (c) 2015年 superbil.org All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface NSUserDefaults (DefaultHackfoldrPage)
12 |
13 | - (NSString *)stringOfDefaultHackfoldrPage;
14 |
15 | - (void)setDefaultHackfoldrPage:(NSString *)aString;
16 |
17 | - (void)removeDefaultHackfolderPage;
18 |
19 | @end
20 |
21 | @interface NSUserDefaults (CurrentHackfoldrPage)
22 |
23 | - (NSString *)stringOfCurrentHackfoldrPage;
24 |
25 | - (void)setCurrentHackfoldrPage:(NSString *)anString;
26 |
27 | - (void)removeCurrentHackfoldrPage;
28 |
29 | @end
30 |
--------------------------------------------------------------------------------
/hackfoldr-iOSTests/hackfoldr-iOSTests-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 | CFBundlePackageType
14 | BNDL
15 | CFBundleShortVersionString
16 | 1.0
17 | CFBundleSignature
18 | ????
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: objective-c
2 | osx_image: xcode9.1
3 |
4 | branches:
5 | only:
6 | - master
7 | - develop
8 |
9 | env:
10 | global:
11 | - COCOAPODS_DISABLE_DETERMINISTIC_UUIDS=1
12 |
13 | before_install:
14 | - gem install xcpretty -N --no-ri --no-rdoc
15 | - SIMULATOR_ID=$(xcrun instruments -s devices | grep -io "$SIMULATOR \[.*\]" | grep -o "\[.*\]" | sed "s/^\[\(.*\)\]$/\1/")
16 | - pod repo update
17 |
18 | script:
19 | - set -o pipefail
20 | - open -b com.apple.iphonesimulator --args -CurrentDeviceUDID $SIMULATOR_ID
21 | - xcodebuild clean build -workspace hackfoldr-iOS.xcworkspace -scheme hackfoldr-iOS -sdk iphonesimulator GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES | xcpretty
22 |
23 | after_success:
24 | - bash <(curl -s https://codecov.io/bash)
25 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/HackfoldrField.h:
--------------------------------------------------------------------------------
1 | //
2 | // HackfoldrField.h
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2014/6/22.
6 | // Copyright (c) 2014年 org.superbil. All rights reserved.
7 | //
8 |
9 | @interface HackfoldrField : NSObject
10 |
11 | - (instancetype)initWithFieldArray:(NSArray *)fields;
12 |
13 | @property (nonatomic, assign) NSUInteger index;
14 | @property (nonatomic, copy) NSString *urlString;
15 | @property (nonatomic, copy) NSString *name;
16 | @property (nonatomic, copy) NSString *actions;
17 |
18 | @property (nonatomic, copy) NSString *labelString;
19 | @property (nonatomic, strong) UIColor *labelColor;
20 |
21 | @property (nonatomic, assign) BOOL isSubItem;
22 | @property (nonatomic, assign) BOOL isCommentLine;
23 |
24 | @property (nonatomic, strong) NSMutableArray *subFields;
25 |
26 | - (BOOL)isEmpty;
27 |
28 | @end
29 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/HackfoldrPage.h:
--------------------------------------------------------------------------------
1 | //
2 | // HackfoldrPage.h
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2014/6/22.
6 | // Copyright (c) 2014年 org.superbil. All rights reserved.
7 | //
8 |
9 | #import "HackfoldrField.h"
10 |
11 | @interface HackfoldrPage : NSObject
12 |
13 | - (instancetype)initWithKey:(NSString *)hackfoldrKey fieldArray:(NSArray *)fieldArray;
14 |
15 | - (instancetype)initWithFieldArray:(NSArray *)fieldArray;
16 |
17 | @property (nonatomic, strong, readonly) NSString *key;
18 |
19 | @property (nonatomic, strong) NSString *pageTitle;
20 |
21 | /// Objcect in NSArray is |HackfoldrField|
22 | @property (nonatomic, strong, readonly) NSArray *cells;
23 |
24 | /**
25 | * rediredKey is redired key from A1
26 | * This is Hackfoldr 2.0 rule
27 | */
28 | @property (nonatomic, strong, readonly) NSString *rediredKey;
29 |
30 | @end
31 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/ListFieldViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ListFieldViewController.h
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2014/8/31.
6 | // Copyright (c) 2014年 org.superbil. All rights reserved.
7 | //
8 |
9 | @class BFTask;
10 | @class RATreeView;
11 | @class HackfoldrPage;
12 | @class HackfoldrTaskCompletionSource;
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface ListFieldViewController : UIViewController
17 |
18 | + (instancetype)viewController;
19 |
20 | @property (nonatomic, strong) RATreeView *treeView;
21 |
22 | @property (nonatomic, strong) IBOutlet UIButton *settingButton;
23 |
24 | @property (nonatomic, assign) BOOL hideBackButton;
25 |
26 | @property (nonatomic, strong) HackfoldrPage *page;
27 |
28 | - (void)reloadPage;
29 |
30 | - (void)updateHackfoldrPageWithKey:(NSString *)hackfoldrKey;
31 |
32 | - (void)showSettingViewController;
33 |
34 | @end
35 |
36 | NS_ASSUME_NONNULL_END
37 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/HackfoldrClient.h:
--------------------------------------------------------------------------------
1 | //
2 | // HackfoldrClient.h
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2014/6/22.
6 | // Copyright (c) 2014年 org.superbil. All rights reserved.
7 | //
8 |
9 | #import "AFHTTPSessionManager.h"
10 | #import "Bolts.h"
11 |
12 | @class HackfoldrPage;
13 |
14 | @interface HackfoldrTaskCompletionSource : BFTaskCompletionSource
15 |
16 | + (HackfoldrTaskCompletionSource *)taskCompletionSource;
17 | @property (strong, nonatomic) NSURLSessionTask *connectionTask;
18 |
19 | @end
20 |
21 |
22 | @interface HackfoldrClient : AFHTTPSessionManager
23 |
24 | + (instancetype)sharedClient;
25 |
26 | - (HackfoldrTaskCompletionSource *)taskCompletionFromEthercalcWithKey:(NSString *)key;
27 |
28 | - (HackfoldrTaskCompletionSource *)taskCompletionFromGoogleSheetWithSheetKey:(NSString *)keyID;
29 |
30 | - (HackfoldrTaskCompletionSource *)taskCompletionWithKey:(NSString *)hackfoldrKey;
31 |
32 | @property (nonatomic, strong, readonly) HackfoldrPage *lastPage;
33 |
34 | @end
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | Copyright © 2016 Kai-Yuan Cheng
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 |
6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7 |
8 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/hackfoldr-iOS/HackoldrModel.xcdatamodeld/HackoldrModel.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/HackfoldrPage+CSSearchableItem.m:
--------------------------------------------------------------------------------
1 | //
2 | // HackfoldrPage+CSSearchableItem.m
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2017/11/2.
6 | //Copyright © 2017年 org.g0v. All rights reserved.
7 | //
8 |
9 | #import "HackfoldrPage+CSSearchableItem.h"
10 |
11 | #import
12 |
13 | @implementation HackfoldrPage (CSSearchableItemAttributeSet)
14 |
15 | - (CSSearchableItemAttributeSet *)searchableAttributeSet {
16 | CSSearchableItemAttributeSet *attributeSet = [[CSSearchableItemAttributeSet alloc] initWithItemContentType:(__bridge NSString *)kUTTypeData];
17 | attributeSet.title = self.pageTitle;
18 | attributeSet.contentDescription = self.key;
19 | attributeSet.keywords = @[@"Hackfoldr", self.key];
20 | return attributeSet;
21 | }
22 |
23 | - (CSSearchableItem *)searchableItemWithDomain:(NSString *)domain {
24 | CSSearchableItem *item = [[CSSearchableItem alloc] initWithUniqueIdentifier:self.key
25 | domainIdentifier:domain
26 | attributeSet:self.searchableAttributeSet];
27 | return item;
28 | }
29 |
30 | @end
31 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // _oo0oo_
3 | // o8888888o
4 | // 88" . "88
5 | // (| -_- |)
6 | // 0\ = /0
7 | // ___/`---'\___
8 | // .' \\| |// '.
9 | // / \\||| : |||// \
10 | // / _||||| -:- |||||- \
11 | // | | \\\ - /// | |
12 | // | \_| ''\---/'' |_/ |
13 | // \ .-\__ '-' ___/-. /
14 | // ___'. .' /--.--\ `. .'___
15 | // ."" '< `.___\_<|>_/___.' >' "".
16 | // | | : `- \`.;`\ _ /`;.`/ - ` : | |
17 | // \ \ `_. \_ __\ /__ _/ .-` / /
18 | // =====`-.____`.___ \_____/___.-`___.-'=====
19 | // `=---='
20 | //
21 | //
22 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23 | //
24 | // 佛祖保佑 永無BUG
25 | //
26 | //
27 | //
28 | //
29 | // AppDelegate.h
30 | // hackfoldr-iOS
31 | //
32 | // Created by Superbil on 2014/6/21.
33 | // Copyright (c) 2014年 org.superbil. All rights reserved.
34 | //
35 |
36 | #import
37 |
38 | @interface AppDelegate : UIResponder
39 |
40 | @property (strong, nonatomic) UIWindow *window;
41 | @property (strong, nonatomic) UIViewController *viewController;
42 |
43 | @end
44 |
--------------------------------------------------------------------------------
/script/xcode-git-version.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # This script automatically sets the version and short version string of
4 | # an Xcode project from the Git repository containing the project.
5 | #
6 | # To use this script in Xcode 4, add the contents to a "Run Script" build
7 | # phase for your application target.
8 |
9 | set -o errexit
10 | set -o nounset
11 |
12 | INFO_PLIST="${BUILT_PRODUCTS_DIR}/${WRAPPER_NAME}/Info"
13 |
14 | # Use the latest version tag for CFBundleShortVersionString. I tag releases
15 | # in Git using the format v0.0.0; this assumes you're doing the same.
16 | # SHORT_VERSION=$(git --git-dir="${PROJECT_DIR}/.git" --work-tree="${PROJECT_DIR}" describe --dirty | sed -e 's/^v//' -e 's/g//')
17 |
18 | # I'd like to use the Git commit hash for CFBundleVersion.
19 | # VERSION=$(git --git-dir="${PROJECT_DIR}/.git" --work-tree="${PROJECT_DIR}" rev-parse --short HEAD)
20 |
21 | # But Apple wants this value to be a monotonically increasing integer, so
22 | # instead use the number of commits on the master branch. If you like to
23 | # play fast and loose with your Git history, this may cause you problems.
24 | # Thanks to @amrox for pointing out the issue and fix.
25 | VERSION=$(git --git-dir="${PROJECT_DIR}/.git" --work-tree="${PROJECT_DIR}" rev-list master | wc -l)
26 |
27 | # defaults write $INFO_PLIST CFBundleShortVersionString $SHORT_VERSION
28 | defaults write $INFO_PLIST CFBundleVersion $VERSION
29 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/g0v.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/NSUserDefaults+DefaultHackfoldrPage.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSUserDefaults+DefaultHackfoldrPage.m
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2015/1/28.
6 | // Copyright (c) 2015年 superbil.org All rights reserved.
7 | //
8 |
9 | #import "NSUserDefaults+DefaultHackfoldrPage.h"
10 |
11 | static NSString *kDefaultHackfoldrPage = @"Default Hackfoldr Page";
12 | static NSString *kCurrentHackfoldrPage = @"Current Hackfoldr Page";
13 |
14 | @implementation NSUserDefaults (DefaultHackfoldrPage)
15 |
16 | - (NSString *)stringOfDefaultHackfoldrPage
17 | {
18 | return [self objectForKey:kDefaultHackfoldrPage];
19 | }
20 |
21 | - (void)setDefaultHackfoldrPage:(NSString *)aString
22 | {
23 | [self setObject:aString forKey:kDefaultHackfoldrPage];
24 | }
25 |
26 | - (void)removeDefaultHackfolderPage
27 | {
28 | [self removeObjectForKey:kDefaultHackfoldrPage];
29 | }
30 |
31 | @end
32 |
33 | @implementation NSUserDefaults (CurrentHackfoldrPage)
34 |
35 | - (NSString *)stringOfCurrentHackfoldrPage
36 | {
37 | NSString *current = [self objectForKey:kCurrentHackfoldrPage];
38 | if (current && current.length > 0) {
39 | return current;
40 | }
41 | // When |currentHackfoldrPage| can't find, use defualt
42 | return [self stringOfDefaultHackfoldrPage];
43 | }
44 |
45 | - (void)setCurrentHackfoldrPage:(NSString *)anString
46 | {
47 | [self setObject:anString forKey:kCurrentHackfoldrPage];
48 | }
49 |
50 | - (void)removeCurrentHackfoldrPage
51 | {
52 | [self removeObjectForKey:kCurrentHackfoldrPage];
53 | }
54 |
55 | @end
56 |
--------------------------------------------------------------------------------
/Changelog.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | ### 1.5
4 |
5 | - Fix warning, change to use new API
6 | - Update pods
7 |
8 | ### 1.4
9 |
10 | - Fix iPhone X layout problem
11 | - Fix table view will show two times
12 | - Add share QR code and scan QR code
13 | - Change to use XLForm
14 | - Update pods
15 |
16 | ### 1.3
17 |
18 | - Fix problem about display on iPhone Plus
19 |
20 | ### 1.2
21 |
22 | - Show list like trees [#20](https://github.com/hackfoldr/hackfoldr-iOS/issues/20)
23 | - Update icons, using [Font Awesome](http://fontawesome.io)
24 | - Support customized links, now one can use `hackfoldr://` to open hackfoldr [#18](https://github.com/hackfoldr/hackfoldr-iOS/issues/18)
25 | - Fix some labels with incorrect content [#22](https://github.com/hackfoldr/hackfoldr-iOS/issues/22)
26 | - Support subField with custom icons (Font Awesome) [#27](https://github.com/hackfoldr/hackfoldr-iOS/issues/27)
27 | - Show page details when link is linking to another hackfoldr page
28 | - Update pods
29 |
30 | ### 1.1
31 |
32 | - Support Safari
33 | - Update pods
34 |
35 | ### 1.0
36 |
37 | - Everything is fine, release app
38 |
39 | ## 1.0 beta-4
40 |
41 | - 修正網址的檢查方式
42 | - 讓 status bar 會顯示
43 | - 更新 QuickDialog
44 |
45 | ## 1.0 beta-3
46 |
47 | - 修正使用 redired A1 一定會 crash 的問題
48 | - 新增中文語系介面
49 |
50 | ## 1.0 beta-2
51 |
52 | - 修正 push list view 和 setting view 造成 crash 的問題
53 |
54 | ## 1.0 beta-1
55 |
56 | - 支援 Google sheets (key 超過40)
57 | - 支援 hackfoldr A1 重導向頁面功能
58 | 可使用 hackfoldr-iOS-testA1 測試
59 | - 重新調整使用流程
60 | - 支援 Travis CI
61 | - 支援使用 hackfoldr 1.0 and 2.0 的欄位定義
62 | - 支援使用 http://hackfoldr.org/ 的方式來設定新的頁面
63 | - 支援 page 歷史記錄
64 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/NSURL+Hackfoldr.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSURL+Hackfoldr.m
3 | // hackfoldr-iOS
4 | //
5 | // Created by 舒特比 on 2016/12/17.
6 | // Copyright © 2016年 org.g0v. All rights reserved.
7 | //
8 |
9 | #import "NSURL+Hackfoldr.h"
10 |
11 | @implementation NSURL (Hackfoldr)
12 |
13 | + (BOOL)canHandleHackfoldrURL:(nonnull NSURL *)url
14 | {
15 | if ([url.scheme isEqualToString:@"hackfoldr"]) {
16 | return YES;
17 | } else if ([url.host isEqualToString:@"hackfoldr.org"]) {
18 | if ([url.path isEqualToString:@"/about"]) {
19 | return NO;
20 | }
21 | return YES;
22 | } else if ([url.host isEqualToString:@"beta.hackfoldr.org"]) {
23 | return YES;
24 | }
25 | return NO;
26 | }
27 |
28 | + (nullable NSString *)realKeyOfHackfoldrWithURL:(nonnull NSURL *)url
29 | {
30 | if ([url.scheme isEqualToString:@"hackfoldr"]) {
31 | return url.host;
32 | } else if ([url.host isEqualToString:@"hackfoldr.org"] || [url.host isEqualToString:@"beta.hackfoldr.org"]) {
33 | return [url.path stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"/"]];
34 | }
35 | return nil;
36 | }
37 |
38 | + (NSString *)validatorHackfoldrKey:(NSString *)hackfoldrKey
39 | {
40 | NSString *newHackfoldrKey = [hackfoldrKey copy];
41 | // Find hackfoldr page key
42 | if ([newHackfoldrKey rangeOfString:@"://"].location != NSNotFound) {
43 | NSURL *hackfoldrURL = [NSURL URLWithString:newHackfoldrKey];
44 | if ([NSURL canHandleHackfoldrURL:hackfoldrURL]) {
45 | newHackfoldrKey = [NSURL realKeyOfHackfoldrWithURL:hackfoldrURL];
46 | }
47 | }
48 |
49 | // Remove white space and new line
50 | newHackfoldrKey = [newHackfoldrKey stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
51 | // Use escapes to encoding |newHackfoldrPage|
52 | newHackfoldrKey = [newHackfoldrKey stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]];
53 | return newHackfoldrKey;
54 | }
55 |
56 | @end
57 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/HackfoldrClient+Store.m:
--------------------------------------------------------------------------------
1 | //
2 | // HackfoldrClient+Store.m
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2017/3/17.
6 | // Copyright © 2017年 org.g0v. All rights reserved.
7 | //
8 |
9 | #import "HackfoldrClient+Store.h"
10 |
11 | #import
12 |
13 | #import "HackfoldrHistory.h"
14 | #import "HackfoldrPage.h"
15 |
16 | @implementation HackfoldrClient (Store)
17 |
18 | - (HackfoldrTaskCompletionSource *)hackfoldrPageTaskWithKey:(NSString *)hackfoldrKey rediredKey:(NSString *)rediredKey
19 | {
20 | NSString *key = hackfoldrKey;
21 | if (rediredKey) {
22 | key = rediredKey;
23 | }
24 |
25 | HackfoldrTaskCompletionSource *s = [[HackfoldrClient sharedClient] taskCompletionWithKey:key];
26 | [[s.task continueWithSuccessBlock:^id _Nullable(BFTask * _Nonnull t) {
27 | HackfoldrPage *page = t.result;
28 |
29 | if (page.rediredKey) {
30 | NSLog(@"redired to:%@", page.rediredKey);
31 | return [self hackfoldrPageTaskWithKey:page.key rediredKey:page.rediredKey].task;
32 | }
33 |
34 | NSLog(@"page: %@", page);
35 | return t;
36 | }] continueWithSuccessBlock:^id _Nullable(BFTask * _Nonnull t) {
37 | HackfoldrPage *page = t.result;
38 |
39 | // Save |history| to core data
40 | HackfoldrHistory *history = [HackfoldrHistory MR_findFirstByAttribute:@"hackfoldrKey" withValue:page.key];
41 | if (!history) {
42 | history = [HackfoldrHistory MR_createEntity];
43 | history.createDate = [NSDate date];
44 | history.refreshDate = [NSDate date];
45 | history.hackfoldrKey = page.key;
46 | history.title = page.pageTitle;
47 | if (page.rediredKey) {
48 | history.rediredKey = page.rediredKey;
49 | }
50 | } else {
51 | history.refreshDate = [NSDate date];
52 | history.title = page.pageTitle;
53 | if (page.rediredKey) {
54 | history.rediredKey = page.rediredKey;
55 | }
56 | }
57 |
58 | [[NSManagedObjectContext MR_defaultContext] MR_saveOnlySelfWithCompletion:nil];
59 | return t;
60 | }];
61 | return s;
62 | }
63 |
64 | @end
65 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/hackfoldr-iOS-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | hackfoldr
9 | CFBundleExecutable
10 | ${EXECUTABLE_NAME}
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | hackfoldr
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.5
21 | CFBundleSignature
22 | ????
23 | CFBundleURLTypes
24 |
25 |
26 | CFBundleTypeRole
27 | Editor
28 | CFBundleURLName
29 | org.g0v.hackfoldr
30 | CFBundleURLSchemes
31 |
32 | hackfoldr
33 |
34 |
35 |
36 | CFBundleVersion
37 | 1
38 | Fabric
39 |
40 | APIKey
41 | aa4bea7059a82c8870fd136c48413ae4cd41082c
42 | Kits
43 |
44 |
45 | KitInfo
46 |
47 | KitName
48 | Crashlytics
49 |
50 |
51 |
52 | ITSAppUsesNonExemptEncryption
53 |
54 | LSRequiresIPhoneOS
55 |
56 | NSCameraUsageDescription
57 | Scan QR code
58 | NSPhotoLibraryUsageDescription
59 | Access photo library
60 | UILaunchStoryboardName
61 | LaunchScreen
62 | UIRequiredDeviceCapabilities
63 |
64 | armv7
65 |
66 | UIStatusBarHidden
67 |
68 | UISupportedInterfaceOrientations
69 |
70 | UIInterfaceOrientationPortrait
71 | UIInterfaceOrientationLandscapeLeft
72 | UIInterfaceOrientationLandscapeRight
73 | UIInterfaceOrientationPortraitUpsideDown
74 |
75 | UISupportedInterfaceOrientations~ipad
76 |
77 | UIInterfaceOrientationPortrait
78 | UIInterfaceOrientationPortraitUpsideDown
79 | UIInterfaceOrientationLandscapeLeft
80 | UIInterfaceOrientationLandscapeRight
81 |
82 | UIViewControllerBasedStatusBarAppearance
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/LaunchScreen.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 |
46 |
--------------------------------------------------------------------------------
/hackfoldr-iOS.xcodeproj/xcshareddata/xcschemes/hackfoldr-iOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
66 |
72 |
73 |
74 |
75 |
76 |
77 |
83 |
85 |
91 |
92 |
93 |
94 |
96 |
97 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - AFCSVParserResponseSerializer (1.0.0):
3 | - AFNetworking (~> 3.0)
4 | - CHCSVParser (~> 2.0)
5 | - AFNetworking (3.2.1):
6 | - AFNetworking/NSURLSession (= 3.2.1)
7 | - AFNetworking/Reachability (= 3.2.1)
8 | - AFNetworking/Security (= 3.2.1)
9 | - AFNetworking/Serialization (= 3.2.1)
10 | - AFNetworking/UIKit (= 3.2.1)
11 | - AFNetworking/NSURLSession (3.2.1):
12 | - AFNetworking/Reachability
13 | - AFNetworking/Security
14 | - AFNetworking/Serialization
15 | - AFNetworking/Reachability (3.2.1)
16 | - AFNetworking/Security (3.2.1)
17 | - AFNetworking/Serialization (3.2.1)
18 | - AFNetworking/UIKit (3.2.1):
19 | - AFNetworking/NSURLSession
20 | - Bolts (1.9.0):
21 | - Bolts/AppLinks (= 1.9.0)
22 | - Bolts/Tasks (= 1.9.0)
23 | - Bolts/AppLinks (1.9.0):
24 | - Bolts/Tasks
25 | - Bolts/Tasks (1.9.0)
26 | - CHCSVParser (2.1.0)
27 | - FontAwesomeKit (2.2.1):
28 | - FontAwesomeKit/Core (= 2.2.1)
29 | - FontAwesomeKit/FontAwesome (= 2.2.1)
30 | - FontAwesomeKit/FoundationIcons (= 2.2.1)
31 | - FontAwesomeKit/IonIcons (= 2.2.1)
32 | - FontAwesomeKit/Material (= 2.2.1)
33 | - FontAwesomeKit/Octicons (= 2.2.1)
34 | - FontAwesomeKit/Zocial (= 2.2.1)
35 | - FontAwesomeKit/Core (2.2.1)
36 | - FontAwesomeKit/FontAwesome (2.2.1):
37 | - FontAwesomeKit/Core
38 | - FontAwesomeKit/FoundationIcons (2.2.1):
39 | - FontAwesomeKit/Core
40 | - FontAwesomeKit/IonIcons (2.2.1):
41 | - FontAwesomeKit/Core
42 | - FontAwesomeKit/Material (2.2.1):
43 | - FontAwesomeKit/Core
44 | - FontAwesomeKit/Octicons (2.2.1):
45 | - FontAwesomeKit/Core
46 | - FontAwesomeKit/Zocial (2.2.1):
47 | - FontAwesomeKit/Core
48 | - MagicalRecord (2.3.2):
49 | - MagicalRecord/Core (= 2.3.2)
50 | - MagicalRecord/Core (2.3.2)
51 | - OHHTTPStubs (8.0.0):
52 | - OHHTTPStubs/Default (= 8.0.0)
53 | - OHHTTPStubs/Core (8.0.0)
54 | - OHHTTPStubs/Default (8.0.0):
55 | - OHHTTPStubs/Core
56 | - OHHTTPStubs/JSON
57 | - OHHTTPStubs/NSURLSession
58 | - OHHTTPStubs/OHPathHelpers
59 | - OHHTTPStubs/JSON (8.0.0):
60 | - OHHTTPStubs/Core
61 | - OHHTTPStubs/NSURLSession (8.0.0):
62 | - OHHTTPStubs/Core
63 | - OHHTTPStubs/OHPathHelpers (8.0.0)
64 | - PocketSVG (2.4.2)
65 | - RATreeView (2.1.2)
66 | - TOWebViewController (2.2.8):
67 | - TOWebViewController/Core (= 2.2.8)
68 | - TOWebViewController/Core (2.2.8)
69 | - XLForm (4.0.1)
70 |
71 | DEPENDENCIES:
72 | - AFCSVParserResponseSerializer
73 | - AFNetworking (~> 3.1)
74 | - AFNetworking/UIKit
75 | - Bolts (~> 1.0)
76 | - FontAwesomeKit (from `https://github.com/PrideChung/FontAwesomeKit.git`)
77 | - FontAwesomeKit/FontAwesome (from `https://github.com/PrideChung/FontAwesomeKit.git`)
78 | - MagicalRecord (~> 2.2)
79 | - OHHTTPStubs
80 | - PocketSVG
81 | - RATreeView
82 | - TOWebViewController
83 | - XLForm (~> 4.0.0)
84 |
85 | SPEC REPOS:
86 | https://github.com/cocoapods/specs.git:
87 | - AFCSVParserResponseSerializer
88 | - AFNetworking
89 | - Bolts
90 | - CHCSVParser
91 | - MagicalRecord
92 | - OHHTTPStubs
93 | - PocketSVG
94 | - RATreeView
95 | - TOWebViewController
96 | - XLForm
97 |
98 | EXTERNAL SOURCES:
99 | FontAwesomeKit:
100 | :git: https://github.com/PrideChung/FontAwesomeKit.git
101 |
102 | CHECKOUT OPTIONS:
103 | FontAwesomeKit:
104 | :commit: ed5eb02f4bcf76e218bb1c966c551a7b5240b6b4
105 | :git: https://github.com/PrideChung/FontAwesomeKit.git
106 |
107 | SPEC CHECKSUMS:
108 | AFCSVParserResponseSerializer: ff1f90868576cbfbedda7f0c2f31361af232b144
109 | AFNetworking: b6f891fdfaed196b46c7a83cf209e09697b94057
110 | Bolts: ac6567323eac61e203f6a9763667d0f711be34c8
111 | CHCSVParser: 3bcc7bdb72111a31222e95d57eacceefe4e537e8
112 | FontAwesomeKit: 3614979b3dbb0ec3f7fac1bd760b91f66c31db67
113 | MagicalRecord: 53bed74b4323b930992a725be713e53b37d19755
114 | OHHTTPStubs: 9cbce6364bec557cc3439aa6bb7514670d780881
115 | PocketSVG: c30139829206a43df2ec203880e3970531facba7
116 | RATreeView: 380cde0090cfde21dc6ac29137f49d3b768fdd28
117 | TOWebViewController: 953a24619d32f7aa1d9d4098a74885a9236f7348
118 | XLForm: b8d47a9a00fb6166981cb40de7169d70d611e9be
119 |
120 | PODFILE CHECKSUM: 1b60b2d866db7e542bbedcac696f4826fffe25a7
121 |
122 | COCOAPODS: 1.5.3
123 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/HackfoldrPage.m:
--------------------------------------------------------------------------------
1 | //
2 | // HackfoldrPage.m
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2014/6/22.
6 | // Copyright (c) 2014年 org.superbil. All rights reserved.
7 | //
8 |
9 | #import "HackfoldrPage.h"
10 |
11 | #import "HackfoldrField.h"
12 |
13 | @interface HackfoldrPage ()
14 | @property (nonatomic, strong, readwrite) NSString *key;
15 | @property (nonatomic, strong) NSArray *fields;
16 | @property (nonatomic, strong, readwrite) NSString *pagetitle;
17 | @property (nonatomic, strong, readwrite) NSString *rediredKey;
18 | @end
19 |
20 | @implementation HackfoldrPage
21 |
22 | - (instancetype)initWithFieldArray:(NSArray *)fieldArray
23 | {
24 | return [self initWithKey:nil fieldArray:fieldArray];
25 | }
26 |
27 | - (instancetype)initWithKey:(NSString *)hackfoldrKey fieldArray:(NSArray *)fieldArray
28 | {
29 | self = [super init];
30 | if (!self) {
31 | return nil;
32 | }
33 |
34 | self.key = hackfoldrKey;
35 |
36 | [self updateWithArray:fieldArray];
37 |
38 | return self;
39 | }
40 |
41 | - (instancetype)copyWithZone:(NSZone *)zone
42 | {
43 | HackfoldrPage *copy = [[HackfoldrPage allocWithZone:zone] init];
44 | copy.key = [self.key copy];
45 | copy.fields = [self.fields copy];
46 | copy.pageTitle = [self.pageTitle copy];
47 | copy.rediredKey = [self.rediredKey copy];
48 | return copy;
49 | }
50 |
51 | - (NSArray *)cells
52 | {
53 | return self.fields;
54 | }
55 |
56 | - (void)updateWithArray:(NSArray *)fieldArray
57 | {
58 | if (!fieldArray || fieldArray.count == 0) {
59 | return;
60 | }
61 |
62 | // Check this page is redired page
63 | NSString *a1String = ((NSArray *)fieldArray.firstObject).firstObject;
64 | a1String = [a1String stringByReplacingOccurrencesOfString:@"\"" withString:@""];
65 | // Check A1 is not comment field and length >= 40
66 | if ([a1String hasPrefix:@"#"] == NO && a1String.length >= 40) {
67 | self.rediredKey = a1String;
68 | // because this is redired page, ignore other things
69 | return;
70 | }
71 |
72 | NSMutableArray *sectionFields = [NSMutableArray array];
73 | __block HackfoldrField *sectionField = nil;
74 | __block BOOL isFindTitle = NO;
75 | [fieldArray enumerateObjectsUsingBlock:^(NSArray *fields, NSUInteger idx, BOOL *stop) {
76 | HackfoldrField *field = [[HackfoldrField alloc] initWithFieldArray:fields];
77 |
78 | if (field.isEmpty || field.isCommentLine) {
79 | return;
80 | }
81 | field.index = idx;
82 |
83 | // find first row isn't comment line and not empty
84 | if (!isFindTitle) {
85 | self.pageTitle = field.name;
86 | isFindTitle = YES;
87 | return;
88 | }
89 |
90 | // other row
91 | if (field.isSubItem == NO) {
92 | // add last |sectionField|
93 | if (sectionField) {
94 | [sectionFields addObject:sectionField];
95 | }
96 |
97 | // Create new section field
98 | // When field have |urlString|, just put into a new section
99 | if (field.urlString.length == 0) {
100 | sectionField = field;
101 | } else {
102 | sectionField = [[HackfoldrField alloc] init];
103 | [sectionField.subFields addObject:field];
104 | }
105 | } else {
106 | // section could be nil
107 | if (!sectionField) {
108 | sectionField = [[HackfoldrField alloc] init];
109 | }
110 | // add |field| to subFields
111 | [sectionField.subFields addObject:field];
112 | }
113 | }];
114 | // Check every section is been add or not
115 | if (sectionField) {
116 | [sectionFields addObject:sectionField];
117 | }
118 |
119 | self.fields = sectionFields;
120 | }
121 |
122 | - (NSString *)description
123 | {
124 | NSMutableString *description = [NSMutableString string];
125 | [description appendFormat:@"key: %@\n", self.key];
126 | [description appendFormat:@"pageTitle: %@\n", self.pageTitle];
127 | if (self.rediredKey) {
128 | [description appendFormat:@"rediredKey: %@", self.rediredKey];
129 | }
130 | // [description appendFormat:@"cells: %@", self.fields];
131 | return description;
132 | }
133 |
134 | @end
135 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "size" : "29x29",
15 | "idiom" : "iphone",
16 | "filename" : "Icon-Small@2x-1.png",
17 | "scale" : "2x"
18 | },
19 | {
20 | "size" : "29x29",
21 | "idiom" : "iphone",
22 | "filename" : "Icon-Small@3x.png",
23 | "scale" : "3x"
24 | },
25 | {
26 | "size" : "40x40",
27 | "idiom" : "iphone",
28 | "filename" : "Icon-Small-40@2x.png",
29 | "scale" : "2x"
30 | },
31 | {
32 | "size" : "40x40",
33 | "idiom" : "iphone",
34 | "filename" : "Icon-Small-40@3x.png",
35 | "scale" : "3x"
36 | },
37 | {
38 | "size" : "60x60",
39 | "idiom" : "iphone",
40 | "filename" : "Icon-60@2x.png",
41 | "scale" : "2x"
42 | },
43 | {
44 | "size" : "60x60",
45 | "idiom" : "iphone",
46 | "filename" : "Icon-60@3x.png",
47 | "scale" : "3x"
48 | },
49 | {
50 | "idiom" : "ipad",
51 | "size" : "20x20",
52 | "scale" : "1x"
53 | },
54 | {
55 | "idiom" : "ipad",
56 | "size" : "20x20",
57 | "scale" : "2x"
58 | },
59 | {
60 | "size" : "29x29",
61 | "idiom" : "ipad",
62 | "filename" : "Icon-Small.png",
63 | "scale" : "1x"
64 | },
65 | {
66 | "size" : "29x29",
67 | "idiom" : "ipad",
68 | "filename" : "Icon-Small@2x.png",
69 | "scale" : "2x"
70 | },
71 | {
72 | "size" : "40x40",
73 | "idiom" : "ipad",
74 | "filename" : "Icon-Small-40.png",
75 | "scale" : "1x"
76 | },
77 | {
78 | "size" : "40x40",
79 | "idiom" : "ipad",
80 | "filename" : "Icon-Small-40@2x-1.png",
81 | "scale" : "2x"
82 | },
83 | {
84 | "size" : "76x76",
85 | "idiom" : "ipad",
86 | "filename" : "Icon-76.png",
87 | "scale" : "1x"
88 | },
89 | {
90 | "size" : "76x76",
91 | "idiom" : "ipad",
92 | "filename" : "Icon-76@2x.png",
93 | "scale" : "2x"
94 | },
95 | {
96 | "size" : "83.5x83.5",
97 | "idiom" : "ipad",
98 | "filename" : "icon-83.5@2x.png",
99 | "scale" : "2x"
100 | },
101 | {
102 | "size" : "1024x1024",
103 | "idiom" : "ios-marketing",
104 | "filename" : "iTunesArtwork@2x.png",
105 | "scale" : "1x"
106 | },
107 | {
108 | "size" : "24x24",
109 | "idiom" : "watch",
110 | "scale" : "2x",
111 | "role" : "notificationCenter",
112 | "subtype" : "38mm"
113 | },
114 | {
115 | "size" : "27.5x27.5",
116 | "idiom" : "watch",
117 | "scale" : "2x",
118 | "role" : "notificationCenter",
119 | "subtype" : "42mm"
120 | },
121 | {
122 | "size" : "29x29",
123 | "idiom" : "watch",
124 | "role" : "companionSettings",
125 | "scale" : "2x"
126 | },
127 | {
128 | "size" : "29x29",
129 | "idiom" : "watch",
130 | "role" : "companionSettings",
131 | "scale" : "3x"
132 | },
133 | {
134 | "size" : "40x40",
135 | "idiom" : "watch",
136 | "scale" : "2x",
137 | "role" : "appLauncher",
138 | "subtype" : "38mm"
139 | },
140 | {
141 | "size" : "44x44",
142 | "idiom" : "watch",
143 | "scale" : "2x",
144 | "role" : "appLauncher",
145 | "subtype" : "40mm"
146 | },
147 | {
148 | "size" : "50x50",
149 | "idiom" : "watch",
150 | "scale" : "2x",
151 | "role" : "appLauncher",
152 | "subtype" : "44mm"
153 | },
154 | {
155 | "size" : "86x86",
156 | "idiom" : "watch",
157 | "scale" : "2x",
158 | "role" : "quickLook",
159 | "subtype" : "38mm"
160 | },
161 | {
162 | "size" : "98x98",
163 | "idiom" : "watch",
164 | "scale" : "2x",
165 | "role" : "quickLook",
166 | "subtype" : "42mm"
167 | },
168 | {
169 | "size" : "108x108",
170 | "idiom" : "watch",
171 | "scale" : "2x",
172 | "role" : "quickLook",
173 | "subtype" : "44mm"
174 | },
175 | {
176 | "idiom" : "watch-marketing",
177 | "size" : "1024x1024",
178 | "scale" : "1x"
179 | }
180 | ],
181 | "info" : {
182 | "version" : 1,
183 | "author" : "xcode"
184 | }
185 | }
--------------------------------------------------------------------------------
/hackfoldr-iOS/HackfoldrClient.m:
--------------------------------------------------------------------------------
1 | //
2 | // HackfoldrClient.m
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2014/6/22.
6 | // Copyright (c) 2014年 org.superbil. All rights reserved.
7 | //
8 |
9 | #import "HackfoldrClient.h"
10 |
11 | #import "AFCSVParserResponseSerializer.h"
12 | #import "HackfoldrPage.h"
13 |
14 | @implementation HackfoldrTaskCompletionSource
15 |
16 | + (HackfoldrTaskCompletionSource *)taskCompletionSource
17 | {
18 | return [[HackfoldrTaskCompletionSource alloc] init];
19 | }
20 |
21 | - (void)dealloc
22 | {
23 | [self.connectionTask cancel];
24 | self.connectionTask = nil;
25 | }
26 |
27 | - (void)cancel
28 | {
29 | [self.connectionTask cancel];
30 | [super cancel];
31 | }
32 |
33 | @end
34 |
35 | #pragma mark -
36 |
37 | @interface HackfoldrClient ()
38 | @property (nonatomic, strong, readwrite) HackfoldrPage *lastPage;
39 | @end
40 |
41 | @implementation HackfoldrClient
42 |
43 | + (instancetype)sharedClient
44 | {
45 | static dispatch_once_t onceToken;
46 | static HackfoldrClient *shareClient;
47 | dispatch_once(&onceToken, ^{
48 | shareClient = [[HackfoldrClient alloc] initWithBaseURL:[NSURL URLWithString:@"https://ethercalc.org/"]];
49 | });
50 | return shareClient;
51 | }
52 |
53 | + (AFCSVParserResponseSerializer *)CSVSerializer
54 | {
55 | AFCSVParserResponseSerializer *serializer = [AFCSVParserResponseSerializer serializer];
56 | serializer.usedEncoding = NSUTF8StringEncoding;
57 | return serializer;
58 | }
59 |
60 | - (instancetype)initWithBaseURL:(NSURL *)url
61 | {
62 | self = [super initWithBaseURL:url];
63 | if (self) {
64 | self.requestSerializer = [AFHTTPRequestSerializer serializer];
65 | self.responseSerializer = [AFJSONResponseSerializer serializer];
66 | }
67 | return self;
68 | }
69 |
70 | - (HackfoldrTaskCompletionSource *)_taskCompletionWithPath:(NSString *)inPath
71 | {
72 | HackfoldrTaskCompletionSource *source = [HackfoldrTaskCompletionSource taskCompletionSource];
73 | NSString *requestPath = [NSString stringWithFormat:@"%@.csv.json", inPath];
74 | source.connectionTask = [self GET:requestPath parameters:nil progress:nil success:^(NSURLSessionDataTask *task, id fieldArray) {
75 | HackfoldrPage *page = [[HackfoldrPage alloc] initWithKey:inPath fieldArray:fieldArray];
76 | self->_lastPage = [page copy];
77 | [source setResult:self->_lastPage];
78 | } failure:^(NSURLSessionDataTask *task, NSError *error) {
79 | [source setError:error];
80 | }];
81 | return source;
82 | }
83 |
84 | - (HackfoldrTaskCompletionSource *)taskCompletionFromEthercalcWithKey:(NSString *)key
85 | {
86 | return [self _taskCompletionWithPath:key];
87 | }
88 |
89 | - (HackfoldrTaskCompletionSource *)taskCompletionFromGoogleSheetWithSheetKey:(NSString *)keyID
90 | {
91 | // example at https://docs.google.com/spreadsheets/d/176W720jq1zpjsOcsZTkSmwqhmm_hK4VFINK_aubF8sc/export?format=csv&gid=0
92 | HackfoldrTaskCompletionSource *source = [HackfoldrTaskCompletionSource taskCompletionSource];
93 |
94 | AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:@"https://docs.google.com/"]];
95 | manager.responseSerializer = [[self class] CSVSerializer];
96 |
97 | NSString *requestKeyID = [keyID stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLPathAllowedCharacterSet]];
98 | NSString *requestPath = [NSString stringWithFormat:@"spreadsheets/d/%@/export?format=csv&gid=0", requestKeyID];
99 | source.connectionTask = [manager GET:requestPath parameters:nil progress:nil success:^(NSURLSessionDataTask *task, id csvFieldArray) {
100 | HackfoldrPage *page = [[HackfoldrPage alloc] initWithKey:keyID fieldArray:csvFieldArray];
101 | self->_lastPage = [page copy];
102 | [source setResult:self->_lastPage];
103 | } failure:^(NSURLSessionDataTask *task, NSError *error) {
104 | NSLog(@"error:%@ %@", error, task.response);
105 | [source setError:error];
106 | }];
107 |
108 | return source;
109 | }
110 |
111 | - (HackfoldrTaskCompletionSource *)taskCompletionWithKey:(NSString *)hackfoldrKey
112 | {
113 | HackfoldrTaskCompletionSource *completionSource = nil;
114 | // check where the data come from, ethercalc or gsheet
115 | if (hackfoldrKey.length < 40) {
116 | completionSource = [self taskCompletionFromEthercalcWithKey:hackfoldrKey];
117 | } else {
118 | completionSource = [self taskCompletionFromGoogleSheetWithSheetKey:hackfoldrKey];
119 | }
120 |
121 | return completionSource;
122 | }
123 |
124 | @end
125 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2014/6/21.
6 | // Copyright (c) 2014年 org.superbil. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 |
11 | #import
12 | #import
13 |
14 | #import "MainViewController.h"
15 | #import "NSURL+Hackfoldr.h"
16 |
17 | @implementation AppDelegate
18 |
19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
20 | {
21 | // setup core data
22 | [MagicalRecord setupCoreDataStack];
23 |
24 | self.viewController = [[UINavigationController alloc] initWithRootViewController:[[MainViewController alloc] init]];
25 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
26 | self.window.rootViewController = self.viewController;
27 | [self.window makeKeyAndVisible];
28 |
29 | return YES;
30 | }
31 |
32 | - (void)applicationWillResignActive:(UIApplication *)application
33 | {
34 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
35 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
36 | }
37 |
38 | - (void)applicationDidEnterBackground:(UIApplication *)application
39 | {
40 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
41 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
42 | }
43 |
44 | - (void)applicationWillEnterForeground:(UIApplication *)application
45 | {
46 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
47 | }
48 |
49 | - (void)applicationDidBecomeActive:(UIApplication *)application
50 | {
51 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
52 | }
53 |
54 | - (void)applicationWillTerminate:(UIApplication *)application
55 | {
56 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
57 | }
58 |
59 | - (BOOL)application:(UIApplication *)app handleOpenURL:(nonnull NSURL *)url
60 | {
61 | return [NSURL canHandleHackfoldrURL:url];
62 | }
63 |
64 | - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options
65 | {
66 | NSLog(@"openURL: %@ options: %@", url, options);
67 | return [self tryUpdateHackfoldrPageKeyWithURL:url];
68 | }
69 |
70 | - (BOOL)application:(UIApplication *)application
71 | openURL:(NSURL *)url
72 | sourceApplication:(NSString *)sourceApplication
73 | annotation:(id)annotation
74 | {
75 | NSLog(@"Calling Application Bundle ID: %@", sourceApplication);
76 | NSLog(@"URL scheme:%@", [url scheme]);
77 | NSLog(@"URL query: %@", [url query]);
78 | return [self tryUpdateHackfoldrPageKeyWithURL:url];
79 | }
80 |
81 | - (void)updateCurrentHackfoldrPageWithKey:(NSString *)pageKey {
82 | if (!pageKey || pageKey.length == 0) return;
83 |
84 | // Find MainViewController
85 | __block MainViewController *vc = nil;
86 | [((UINavigationController *)self.viewController).viewControllers enumerateObjectsUsingBlock:^(__kindof UIViewController * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
87 | if ([obj isKindOfClass:[MainViewController class]]) {
88 | vc = obj;
89 | *stop = YES;
90 | }
91 | }];
92 | if (!vc) return;
93 |
94 | dispatch_async(dispatch_get_main_queue(), ^{
95 | [vc updateHackfoldrPageWithKey:pageKey];
96 | });
97 | }
98 |
99 | - (BOOL)tryUpdateHackfoldrPageKeyWithURL:(NSURL *)url {
100 | BOOL canHandle = [NSURL canHandleHackfoldrURL:url];
101 | [self updateCurrentHackfoldrPageWithKey:url.host];
102 | return canHandle;
103 | }
104 |
105 | - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray> * _Nullable))restorationHandler {
106 | if ([userActivity.activityType isEqualToString:CSSearchableItemActionType]) {
107 | NSLog(@"CSSearchableItemActionType");
108 | NSString *uniqueIdentifier = userActivity.userInfo[CSSearchableItemActivityIdentifier];
109 | NSLog(@"uniqueIdentifier %@", uniqueIdentifier);
110 | [self updateCurrentHackfoldrPageWithKey:uniqueIdentifier];
111 | }
112 | return YES;
113 | }
114 |
115 | @end
116 |
--------------------------------------------------------------------------------
/hackfoldr-iOSTests/TestData/sample.csv.json:
--------------------------------------------------------------------------------
1 | [
2 | [
3 | "#A1: if not marked as comment (start with #), better less than 40 characters, or will be parsed as a google spreadsheer id",
4 | "#if you want to use google spreadsheet to save foldr data (usually when authorization is needed), fill in A1 with google spreadsheet id, and the same url will be redirected to gsheet.",
5 | "",
6 | ""
7 | ],
8 | [
9 | "#url or item level. <: following links is level 0 (= top level)",
10 | "#title",
11 | "#option: hide/unsortable(foldr title), expand/collapse(subfoldr), blank(link)",
12 | "#label: (color) content. color: gray/black/green/red/blue/orange/purple/teal/yellow/pink/deep-blue/deep-green/deep-purple/"
13 | ],
14 | [
15 | "",
16 | "",
17 | "",
18 | ""
19 | ],
20 | [
21 | "",
22 | "Hackfoldr 使用教學",
23 | "",
24 | ""
25 | ],
26 | [
27 | "https://g0v.hackpad.com/Hackfoldr-2.0--jfLiSxnllO6",
28 | "Hackfoldr 2.0 教學 - 儲存格語法",
29 | "",
30 | ""
31 | ],
32 | [
33 | "https://g0v.hackpad.com/G7idRJqbG3I",
34 | "Hackfoldr 2.0 教學 - 自行架站",
35 | "",
36 | ""
37 | ],
38 | [
39 | "https://g0v.hackpad.com/0.jmm9642fog5vcxr",
40 | "Hackfoldr 2.0 功能列表",
41 | "",
42 | ""
43 | ],
44 | [
45 | "https://g0v.hackpad.com/Hackfoldr-2.0-7TMSpXasDWl",
46 | "Hackfoldr 2.0 專案開發文件",
47 | "",
48 | ""
49 | ],
50 | [
51 | "",
52 | "",
53 | "",
54 | ""
55 | ],
56 | [
57 | "",
58 | "功能示範區",
59 | "expand",
60 | ""
61 | ],
62 | [
63 | "",
64 | "",
65 | "",
66 | ""
67 | ],
68 | [
69 | "lalala",
70 | "#",
71 | "i am marked as comment",
72 | ""
73 | ],
74 | [
75 | "http://logbot.g0v.tw/channel/g0v.tw/today",
76 | "測試 link item",
77 | "blank",
78 | "deep-green label"
79 | ],
80 | [
81 | "http://logbot.g0v.tw/channel/g0v.tw/today",
82 | "測試 link item",
83 | "",
84 | "teal label"
85 | ],
86 | [
87 | "",
88 | "",
89 | "",
90 | ""
91 | ],
92 | [
93 | "http://g0v.tw",
94 | "測試 link item 測試 link item 測試 link item ",
95 | "",
96 | "green label"
97 | ],
98 | [
99 | "http://logbot.g0v.tw/channel/g0v.tw/today",
100 | "測試 link item 測試 link item 測試 link item ",
101 | "blank",
102 | "black label"
103 | ],
104 | [
105 | "http://g0v.tw",
106 | "測試 link item 測試 link item 測試 link item ",
107 | "",
108 | "故事告訴我們 label 不可以太長"
109 | ],
110 | [
111 | "http://logbot.g0v.tw/channel/g0v.tw/today",
112 | "測試 link item",
113 | "",
114 | "blue redmine"
115 | ],
116 | [
117 | "http://logbot.g0v.tw/channel/g0v.tw/today",
118 | "測試 link item",
119 | "",
120 | "green blue island"
121 | ],
122 | [
123 | "http://logbot.g0v.tw/channel/g0v.tw/today",
124 | "測試 link item",
125 | "",
126 | "yellow green island"
127 | ],
128 | [
129 | "http://logbot.g0v.tw/channel/g0v.tw/today",
130 | "測試 link item",
131 | "",
132 | "pink red green blue"
133 | ],
134 | [
135 | "http://logbot.g0v.tw/channel/g0v.tw/today",
136 | "測試 link item",
137 | "",
138 | "purple legendary weapons"
139 | ],
140 | [
141 | "http://logbot.g0v.tw/channel/g0v.tw/today",
142 | "測試 link item",
143 | "",
144 | "lol:warning"
145 | ],
146 | [
147 | "http://logbot.g0v.tw/channel/g0v.tw/today",
148 | "測試 link item",
149 | "",
150 | "nice:important"
151 | ],
152 | [
153 | "http://logbot.g0v.tw/channel/g0v.tw/today",
154 | "測試 link item",
155 | "",
156 | "oops:issue"
157 | ],
158 | [
159 | "https://www.youtube.com/watch?v=WCX1XQ0S9Yk",
160 | "測試 link item",
161 | "",
162 | "green gxv:important"
163 | ],
164 | [
165 | "https://www.flickr.com/photos/othree/sets/72157646878582219",
166 | "活動紀錄 flickr 相簿",
167 | "",
168 | ""
169 | ],
170 | [
171 | "",
172 | "",
173 | "",
174 | ""
175 | ],
176 | [
177 | "",
178 | "",
179 | "",
180 | ""
181 | ],
182 | [
183 | "",
184 | "Code for Kaohsiung 暫存區 (等 mikimoto 搬好家後再刪掉)",
185 | "collapse",
186 | ""
187 | ],
188 | [
189 | "",
190 | "",
191 | "",
192 | ""
193 | ],
194 | [
195 | "",
196 | "進行中",
197 | "expand",
198 | ""
199 | ],
200 | [
201 | "https://codeforkaohsiung.hackpad.com/-Open-Data--76L9xyVlmvN",
202 | "高雄市戶政資料 Open Data 化",
203 | "",
204 | "進行中:important"
205 | ],
206 | [
207 | "",
208 | "",
209 | "",
210 | ""
211 | ],
212 | [
213 | "",
214 | "討論中",
215 | "expand",
216 | ""
217 | ],
218 | [
219 | "https://codeforkaohsiung.hackpad.com/t9Zs76X0xzb",
220 | "CityDashBoard for Kaohsiung",
221 | "",
222 | "green 孵化中"
223 | ],
224 | [
225 | "https://codeforkaohsiung.hackpad.com/0VzJsbx9FfW",
226 | "我有新點子",
227 | "",
228 | "green 孵化中"
229 | ],
230 | [
231 | "",
232 | "",
233 | "",
234 | ""
235 | ],
236 | [
237 | "",
238 | "尋求協助或支援",
239 | "collapse",
240 | ""
241 | ],
242 | [
243 | "https://g0v.hackpad.com/KjfdRZ08FZ3",
244 | "議員投票指南",
245 | "",
246 | "g0v:important"
247 | ],
248 | [
249 | "http://comap.clkao.org/comap/KHH/0989",
250 | "全國投開票所鄉民 Geocode",
251 | "",
252 | "g0v:important"
253 | ],
254 | [
255 | "",
256 | "",
257 | "",
258 | ""
259 | ],
260 | [
261 | "",
262 | "",
263 | "",
264 | ""
265 | ],
266 | [
267 | "https://github.com/codeforkaohsiung/",
268 | "Code for Kaohsiung Github",
269 | "",
270 | ""
271 | ],
272 | [
273 | "http://codeforkaohsiung.slack.com",
274 | "Slack 交流頻道 (需邀請)",
275 | "blank",
276 | ""
277 | ]
278 | ]
--------------------------------------------------------------------------------
/hackfoldr-iOS/QRCodeViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // QRCodeViewController.m
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2017/11/22.
6 | // Copyright © 2017年 org.g0v. All rights reserved.
7 | //
8 |
9 | #import "QRCodeViewController.h"
10 |
11 | #import
12 |
13 | @interface QRCodeViewController ()
14 | @property IBOutlet NSLayoutConstraint *widthOfCenterViewConstraint;
15 | @property IBOutlet UIView *centerView;
16 | @property IBOutlet UIImageView *qrCodeImageView;
17 | @property IBOutlet UIView *cameraView;
18 |
19 | @property AVCaptureDevice *device;
20 | @property AVCaptureSession *session;
21 | @property AVCaptureVideoPreviewLayer *previewLayer;
22 | @end
23 |
24 | @implementation QRCodeViewController
25 |
26 | + (instancetype)viewController {
27 | return [[UIStoryboard storyboardWithName:@"QRCode" bundle:nil] instantiateViewControllerWithIdentifier:@"QRCodeViewController"];
28 | }
29 |
30 | - (void)viewDidLoad {
31 | [super viewDidLoad];
32 | // Do any additional setup after loading the view.
33 | }
34 |
35 | - (void)didReceiveMemoryWarning {
36 | [super didReceiveMemoryWarning];
37 | // Dispose of any resources that can be recreated.
38 | }
39 |
40 | - (void)viewWillAppear:(BOOL)animated {
41 | [super viewWillAppear:animated];
42 |
43 | if (self.qrCodeString) {
44 | self.title = NSLocalizedStringFromTable(@"Show QR Code", @"Hackfoldr", @"title of QR code view controller");
45 | } else {
46 | self.title = NSLocalizedStringFromTable(@"Scan QR Code", @"Hackfoldr", @"title of QR code view controller");
47 | }
48 | }
49 |
50 | - (void)viewDidAppear:(BOOL)animated {
51 | [super viewDidAppear:animated];
52 |
53 | if (self.qrCodeString) {
54 | CGSize size = self.qrCodeImageView.frame.size;
55 | CIImage *ciImage = [self outputNormalImageWithString:self.qrCodeString imageSize:size];
56 | self.qrCodeImageView.image = [self createNonInterpolatedUIImageFormCIImage:ciImage withImageSize:size];
57 | self.cameraView.hidden = YES;
58 | } else {
59 | self.centerView.hidden = YES;
60 | #if !TARGET_IPHONE_SIMULATOR
61 | [self showCapture];
62 | #endif
63 | }
64 | }
65 |
66 | #pragma mark - Private
67 |
68 | - (CIImage *)outputNormalImageWithString:(NSString *)string
69 | imageSize:(CGSize)imageSize {
70 | NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
71 |
72 | CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
73 | [filter setDefaults];
74 | [filter setValue:data forKey:@"inputMessage"];
75 | [filter setValue:@"H" forKey:@"inputCorrectionLevel"];
76 |
77 | return [filter outputImage];
78 | }
79 |
80 | - (UIImage *)createNonInterpolatedUIImageFormCIImage:(CIImage *)image
81 | withImageSize:(CGSize)imageSize {
82 | CGRect extent = CGRectIntegral(image.extent);
83 | CGFloat scale = MIN(imageSize.width / CGRectGetWidth(extent), imageSize.height / CGRectGetHeight(extent));
84 |
85 | size_t width = CGRectGetWidth(extent) * scale;
86 | size_t height = CGRectGetHeight(extent) * scale;
87 | CGColorSpaceRef cs = CGColorSpaceCreateDeviceGray();
88 | CGContextRef bitmapRef = CGBitmapContextCreate(nil, width, height, 8, 0, cs, (CGBitmapInfo)kCGImageAlphaNone);
89 | CIContext *context = [CIContext contextWithOptions:nil];
90 | CGImageRef bitmapImage = [context createCGImage:image fromRect:extent];
91 | CGContextSetInterpolationQuality(bitmapRef, kCGInterpolationNone);
92 | CGContextScaleCTM(bitmapRef, scale, scale);
93 | CGContextDrawImage(bitmapRef, extent, bitmapImage);
94 |
95 | CGImageRef scaledImage = CGBitmapContextCreateImage(bitmapRef);
96 | CGContextRelease(bitmapRef);
97 | CGImageRelease(bitmapImage);
98 |
99 | return [UIImage imageWithCGImage:scaledImage];;
100 | }
101 |
102 | - (void)showCapture {
103 | self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
104 |
105 | AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:nil];
106 | AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
107 | [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
108 |
109 | self.session = [[AVCaptureSession alloc] init];
110 | [self.session setSessionPreset:AVCaptureSessionPresetHigh];
111 |
112 | if ([self.session canAddInput:input]) {
113 | [self.session addInput:input];
114 | }
115 | if ([self.session canAddOutput:output]) {
116 | [self.session addOutput:output];
117 | }
118 |
119 | output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code];
120 |
121 | self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
122 | self.previewLayer.videoGravity = AVLayerVideoGravityResize;;
123 | self.previewLayer.frame = self.view.bounds;
124 |
125 | [self.cameraView.layer insertSublayer:self.previewLayer atIndex:0];
126 | self.centerView.hidden = YES;
127 |
128 | [self.session startRunning];
129 | }
130 |
131 | #pragma mark - AVCaptureMetadataOutputObjectsDelegate
132 |
133 | - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects
134 | fromConnection:(AVCaptureConnection *)connection {
135 | if (metadataObjects != nil && [metadataObjects count] > 0) {
136 | AVMetadataMachineReadableCodeObject *metadataObj = [metadataObjects objectAtIndex:0];
137 | if (metadataObj.stringValue && metadataObj.stringValue.length > 0) {
138 |
139 | [self.session stopRunning];
140 |
141 | if (self.foundedResult) {
142 | self.foundedResult(metadataObj.stringValue);
143 | }
144 | }
145 | }
146 | }
147 |
148 | @end
149 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/QRCode.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 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/MainViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // MainViewController.m
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2014/6/21.
6 | // Copyright (c) 2014年 org.superbil. All rights reserved.
7 | //
8 |
9 | #import "MainViewController.h"
10 |
11 | #import
12 | #import
13 |
14 | #import "AppDelegate.h"
15 | // Model & Client
16 | #import "HackfoldrClient.h"
17 | #import "HackfoldrPage.h"
18 | // Category
19 | #import "HackfoldrClient+Store.h"
20 | #import "NSUserDefaults+DefaultHackfoldrPage.h"
21 | #import "UIColor+Hackfoldr.h"
22 | // ViewController
23 | #import
24 | #import "ListFieldViewController.h"
25 |
26 | @interface MainViewController ()
27 | @property (nonatomic, strong) ListFieldViewController *listViewController;
28 | @property (nonatomic, strong) UIImageView *backgroundImageView;
29 | @end
30 |
31 | @implementation MainViewController
32 |
33 | - (void)viewDidLoad
34 | {
35 | [super viewDidLoad];
36 |
37 | self.title = [[NSBundle mainBundle].infoDictionary objectForKey:@"CFBundleName"];
38 |
39 | UIButton *reloadButton = [UIButton buttonWithType:UIButtonTypeSystem];
40 | CGFloat iconSize = 24;
41 | reloadButton.frame = CGRectMake(0, 0, iconSize, iconSize);
42 | UIImage *reloadImage = [[FAKFontAwesome refreshIconWithSize:iconSize] imageWithSize:reloadButton.frame.size];
43 | [reloadButton setImage:reloadImage forState:UIControlStateNormal];
44 | [reloadButton addTarget:self
45 | action:@selector(reloadAction:)
46 | forControlEvents:UIControlEventTouchUpInside];
47 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:reloadButton];
48 |
49 | self.listViewController = [ListFieldViewController viewController];
50 |
51 | UIImage *backgroundImage = [[UIImage imageNamed:@"hackfoldr-icon"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
52 | self.backgroundImageView = [[UIImageView alloc] initWithImage:backgroundImage];
53 | self.backgroundImageView.tintColor = [UIColor hackfoldrIconWhite];
54 | self.backgroundImageView.backgroundColor = [UIColor clearColor];
55 | [self updateBackgroundImageWithSize:self.view.frame.size];
56 |
57 | if (self.backgroundImageView) {
58 | [self.view addSubview:self.backgroundImageView];
59 | }
60 |
61 | self.view.backgroundColor = [UIColor hackfoldrGreenColor];
62 |
63 | [self mainNavigationController].delegate = self;
64 |
65 | [CSSearchableIndex.defaultSearchableIndex deleteAllSearchableItemsWithCompletionHandler:^(NSError * _Nullable error) {
66 | if (error) {
67 | NSLog(@"deleteAllSearchableItems %@", error);
68 | }
69 | }];
70 | }
71 |
72 | - (void)didReceiveMemoryWarning
73 | {
74 | [super didReceiveMemoryWarning];
75 | }
76 |
77 | #pragma mark - Setter & Getter
78 |
79 | - (UINavigationController *)mainNavigationController
80 | {
81 | return (UINavigationController *)((AppDelegate *)[UIApplication sharedApplication].delegate).viewController;
82 | }
83 |
84 | - (void)updateBackgroundImageWithSize:(CGSize)size
85 | {
86 | CGFloat imageSize = 176.f;
87 | self.backgroundImageView.frame = CGRectMake(size.width/2.f - imageSize/2.f,
88 | size.height/2.f - imageSize/2.f,
89 | imageSize,
90 | imageSize);
91 | }
92 |
93 | - (NSString *)hackfoldrPageKey
94 | {
95 | NSString *pageKey = [[NSUserDefaults standardUserDefaults] stringOfCurrentHackfoldrPage];
96 |
97 | if (!pageKey || pageKey.length == 0) {
98 | NSString *defaultPage = @"hackfoldr-iOS";
99 |
100 | [[NSUserDefaults standardUserDefaults] setDefaultHackfoldrPage:defaultPage];
101 | [[NSUserDefaults standardUserDefaults] synchronize];
102 |
103 | pageKey = [[NSUserDefaults standardUserDefaults] stringOfCurrentHackfoldrPage];
104 | }
105 |
106 | return pageKey;
107 | }
108 |
109 | #pragma mark - View Flow
110 |
111 | - (void)viewDidAppear:(BOOL)animated
112 | {
113 | [super viewDidAppear:animated];
114 |
115 | [self reloadAction:self];
116 | }
117 |
118 | - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator
119 | {
120 | [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
121 |
122 | [self updateBackgroundImageWithSize:size];
123 | }
124 |
125 | #pragma mark - UINavigationControllerDelegate
126 |
127 | - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
128 | {
129 |
130 | }
131 |
132 | #pragma mark - Actions
133 |
134 | - (void)showListViewController
135 | {
136 | // When List view is showed, don't show again
137 | for (UIViewController *vc in self.navigationController.viewControllers) {
138 | if ([vc isKindOfClass:[self.listViewController class]] && vc == self.listViewController) {
139 | [self.navigationController popToViewController:vc animated:YES];
140 | return;
141 | }
142 | }
143 |
144 | [self.navigationController pushViewController:self.listViewController animated:YES];
145 | }
146 |
147 | - (void)reloadAction:(id)sender
148 | {
149 | HackfoldrTaskCompletionSource *completionSource = [[HackfoldrClient sharedClient] hackfoldrPageTaskWithKey:[self hackfoldrPageKey] rediredKey:nil];
150 |
151 | NSString *title = NSLocalizedStringFromTable(@"Error", @"Hackfoldr", @"Error title");
152 | NSString *cancelButtonTitle = NSLocalizedStringFromTable(@"Cancel", @"Hackfoldr", @"Alert Cancel button");
153 | NSString *setupTitle = NSLocalizedStringFromTable(@"Setup Key", @"Hackfoldr", @"Alert Setup button");
154 |
155 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleAlert];
156 | [alert addAction:[UIAlertAction actionWithTitle:setupTitle style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
157 | [self.listViewController showSettingViewController];
158 | [alert dismissViewControllerAnimated:YES completion:nil];
159 | }]];
160 |
161 | [alert addAction:[UIAlertAction actionWithTitle:cancelButtonTitle style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
162 | if ([HackfoldrClient sharedClient].lastPage) {
163 | [self showListViewController];
164 | }
165 | [alert dismissViewControllerAnimated:YES completion:nil];
166 | }]];
167 |
168 | [completionSource.task continueWithBlock:^id _Nullable(BFTask * _Nonnull t) {
169 | if (t.error) {
170 | [self presentViewController:alert animated:YES completion:nil];
171 | }
172 | return nil;
173 | }];
174 |
175 | [completionSource.task continueWithSuccessBlock:^id _Nullable(BFTask * _Nonnull t) {
176 | [self showListViewController];
177 |
178 | HackfoldrPage *page = t.result;
179 | self.listViewController.page = page;
180 | // Reload tableView
181 | [self.listViewController reloadPage];
182 | return nil;
183 | }];
184 | }
185 |
186 | #pragma mark - Public method
187 |
188 | - (void)updateHackfoldrPageWithKey:(NSString *)hackfoldrKey
189 | {
190 | [self.listViewController updateHackfoldrPageWithKey:hackfoldrKey];
191 | }
192 |
193 | @end
194 |
--------------------------------------------------------------------------------
/hackfoldr-iOSTests/hackfoldr_iOSTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // hackfoldr_iOSTests.m
3 | // hackfoldr-iOSTests
4 | //
5 | // Created by Superbil on 2014/6/21.
6 | // Copyright (c) 2014年 org.superbil. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import
12 |
13 | #import "HackfoldrClient.h"
14 | #import "HackfoldrField.h"
15 | #import "HackfoldrPage.h"
16 | #import "NSURL+Hackfoldr.h"
17 |
18 | @interface AnnotatedRequestSerializer : AFHTTPRequestSerializer @end
19 | @implementation AnnotatedRequestSerializer
20 | - (NSMutableURLRequest *)requestWithMethod:(NSString *)method
21 | URLString:(NSString *)URLString
22 | parameters:(NSDictionary *)parameters
23 | error:(NSError * __autoreleasing *)error {
24 | NSMutableURLRequest* req = [super requestWithMethod:method URLString:URLString parameters:parameters error:error];
25 | [NSURLProtocol setProperty:parameters forKey:@"parameters" inRequest:req];
26 | [NSURLProtocol setProperty:req.HTTPBody forKey:@"HTTPBody" inRequest:req];
27 | return req;
28 | }
29 | @end
30 |
31 |
32 | @interface HackfoldrClient (UnitTest)
33 | - (instancetype)initWithBaseURLForUnitTest:(NSURL *)url;
34 | @end
35 |
36 | @implementation HackfoldrClient (UnitTest)
37 |
38 | - (instancetype)initWithBaseURLForUnitTest:(NSURL *)url {
39 | self = [super initWithBaseURL:url];
40 | if (self) {
41 | self.requestSerializer = [AnnotatedRequestSerializer serializer];
42 | self.responseSerializer = [AFJSONResponseSerializer serializer];
43 | }
44 | return self;
45 | }
46 |
47 | @end
48 |
49 |
50 | @interface hackfoldr_iOSTests : XCTestCase
51 | @end
52 |
53 | @implementation hackfoldr_iOSTests
54 |
55 | - (void)setUp
56 | {
57 | [super setUp];
58 | }
59 |
60 | - (void)tearDown
61 | {
62 | [super tearDown];
63 | }
64 |
65 | - (void)testHackfoldrPage
66 | {
67 | HackfoldrPage *page = [[HackfoldrPage alloc] initWithFieldArray:@[@[@"abcdefg_is_key_length_must_bigger_than_40", @"A1"]]];
68 | XCTAssertTrue(page.rediredKey!=nil);
69 | XCTAssertTrue(page.rediredKey.length > 0);
70 |
71 | NSString *comment = @"#A1: if not marked as comment (start with #), better less than 40 characters, or will be parsed as a google spreadsheer id";
72 | HackfoldrPage *commentPage = [[HackfoldrPage alloc] initWithFieldArray:@[@[comment, @"A1 comment"]]];
73 | XCTAssertNil(commentPage.rediredKey);
74 | }
75 |
76 | - (void)testHackfoldrField
77 | {
78 | NSString *nameURL = @"http://www.g0v.tw";
79 | NSString *name = @"Name ";
80 | NSString *actions = @"{target}";
81 | HackfoldrField *field = [[HackfoldrField alloc] initWithFieldArray:@[nameURL, name, actions]];
82 | XCTAssertTrue([field.urlString isEqualToString:nameURL], @"");
83 | XCTAssertTrue([field.name isEqualToString:name], @"");
84 | XCTAssertTrue([field.actions isEqualToString:actions], @"");
85 | XCTAssertTrue(field.isSubItem, @"");
86 |
87 | NSString *subItemURL = @" http://www.g0v.tw";
88 | HackfoldrField *subField = [[HackfoldrField alloc] initWithFieldArray:@[subItemURL, name, actions]];
89 | NSString *subString = [subItemURL substringWithRange:NSMakeRange(1, subItemURL.length-1)];
90 | XCTAssertTrue([subField.urlString isEqualToString:subString], @"");
91 | XCTAssertTrue([subField.name isEqualToString:name], @"");
92 | XCTAssertTrue([subField.actions isEqualToString:actions], @"");
93 | XCTAssertTrue(subField.isSubItem, @"isSubItem must be YES");
94 |
95 | // Hacfoldr 2.0 rule
96 | NSString *topItemURL = @"< http://hackfoldr.org/";
97 | HackfoldrField *topField = [[HackfoldrField alloc] initWithFieldArray:@[topItemURL, name, actions]];
98 | NSString *subStringOfTopField = [topItemURL substringWithRange:NSMakeRange(2, topItemURL.length -2)];
99 | XCTAssertFalse(topField.isSubItem);
100 | XCTAssertTrue([topField.urlString isEqualToString:subStringOfTopField]);
101 |
102 | NSString *commentURL = @"# yooo";
103 | HackfoldrField *commentField = [[HackfoldrField alloc] initWithFieldArray:@[@"", commentURL, actions]];
104 | XCTAssertTrue(commentField.isCommentLine);
105 |
106 | NSString *notCleanCommentURL = @"\"# not yoo\"";
107 | HackfoldrField *notCleanCommentField = [[HackfoldrField alloc] initWithFieldArray:@[@"", notCleanCommentURL, actions]];
108 | XCTAssertTrue(notCleanCommentField.isCommentLine);
109 |
110 | NSString *lableString = @"blue I am red";
111 | HackfoldrField *labelField = [[HackfoldrField alloc] initWithFieldArray:@[nameURL, @"label", actions, lableString]];
112 | XCTAssertTrue([labelField.labelString isEqualToString:@"I am red"]);
113 |
114 | NSString *commentLabelString = @"lol:warning";
115 | HackfoldrField *commentLabelField = [[HackfoldrField alloc] initWithFieldArray:@[nameURL, @"commentLabel", actions, commentLabelString]];
116 | NSLog(@"%@",commentLabelField.labelString);
117 | XCTAssertTrue([commentLabelField.labelString isEqualToString:@"lol"]);
118 | }
119 |
120 | - (void)testHackfoldrClient
121 | {
122 | XCTestExpectation *openHackfoldrExpectation = [self expectationWithDescription:@"open Hackfoldr"];
123 |
124 | id stub = [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
125 | return [request.URL.absoluteString rangeOfString:@"ethercalc.org"].location != NSNotFound;
126 | } withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) {
127 | NSLog(@"hook hackfoldr:%@", request);
128 | NSString *jsonCSVDataString = OHPathForFile(@"sample.csv.json", [self class]);
129 | XCTAssertNotNil(jsonCSVDataString);
130 | NSData *csvData = [NSData dataWithContentsOfFile:jsonCSVDataString];
131 |
132 | return [OHHTTPStubsResponse responseWithData:csvData
133 | statusCode:200
134 | headers:@{@"Content-Type":@"text/json"}];
135 | }];
136 |
137 | HackfoldrClient *client = [[HackfoldrClient alloc] initWithBaseURLForUnitTest:[NSURL URLWithString:@"https://ethercalc.org/"]];
138 | [[client taskCompletionWithKey:@"testHackFoldr"].task continueWithBlock:^id(BFTask *task) {
139 | XCTAssertNil(task.error);
140 | NSLog(@"task %@ %@", task.error, task.result);
141 |
142 | [openHackfoldrExpectation fulfill];;
143 | return nil;
144 | }];
145 |
146 | [self waitForExpectationsWithTimeout:1000 handler:^(NSError * _Nullable error) {
147 | [OHHTTPStubs removeStub:stub];
148 | }];
149 | }
150 |
151 | - (void)testHackfoldrURL {
152 | XCTAssertTrue([NSURL canHandleHackfoldrURL:[NSURL URLWithString:@"hackfoldr://test"]]);
153 | XCTAssertTrue([NSURL canHandleHackfoldrURL:[NSURL URLWithString:@"http://hackfoldr.org"]]);
154 | XCTAssertTrue([NSURL canHandleHackfoldrURL:[NSURL URLWithString:@"http://beta.hackfoldr.org"]]);
155 | XCTAssertTrue([NSURL canHandleHackfoldrURL:[NSURL URLWithString:@"https://hackfoldr.org"]]);
156 | XCTAssertFalse([NSURL canHandleHackfoldrURL:[NSURL URLWithString:@"https://hackfoldr-iOS"]]);
157 | XCTAssertFalse([NSURL canHandleHackfoldrURL:[NSURL URLWithString:@"http://nobody.hackfoldr.org"]]);
158 | }
159 |
160 | - (void)testRealKey {
161 | NSString *realKeyOfHackfoldr = [NSURL realKeyOfHackfoldrWithURL:[NSURL URLWithString:@"hackfoldr://hackfoldr-iOS/"]];
162 | XCTAssertNotNil(realKeyOfHackfoldr);
163 | XCTAssertEqualObjects(realKeyOfHackfoldr, @"hackfoldr-iOS");
164 |
165 | NSString *realKeyOfHackfoldrOrg = [NSURL realKeyOfHackfoldrWithURL:[NSURL URLWithString:@"https://hackfoldr.org/hackfoldr-iOS/"]];
166 | XCTAssertNotNil(realKeyOfHackfoldrOrg);
167 | XCTAssertEqualObjects(realKeyOfHackfoldrOrg, @"hackfoldr-iOS");
168 |
169 | NSString *realKeyOfBeta = [NSURL realKeyOfHackfoldrWithURL:[NSURL URLWithString:@"http://beta.hackfoldr.org/hackfoldr-iOS/"]];
170 | XCTAssertNotNil(realKeyOfBeta);
171 | XCTAssertEqualObjects(realKeyOfBeta, @"hackfoldr-iOS");
172 |
173 | NSString *badRealKey = [NSURL realKeyOfHackfoldrWithURL:[NSURL URLWithString:@"https://hackfoldr-iOS"]];
174 | XCTAssertNil(badRealKey);
175 | }
176 |
177 | @end
178 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/HackfoldrField.m:
--------------------------------------------------------------------------------
1 | //
2 | // HackfoldrField.m
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2014/6/22.
6 | // Copyright (c) 2014年 org.superbil. All rights reserved.
7 | //
8 |
9 | #import "HackfoldrField.h"
10 |
11 | //RGB color macro
12 | #define UIColorFromRGB(rgbValue) [UIColor \
13 | colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \
14 | green:((float)((rgbValue & 0xFF00) >> 8))/255.0 \
15 | blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
16 |
17 | typedef NS_ENUM(NSUInteger, FieldType) {
18 | FieldType_URL = 0,
19 | FieldType_Title,
20 | FieldType_Foldrexpand,
21 | FieldType_Label
22 | };
23 |
24 | @interface HackfoldrField () {
25 | NSString *_urlString;
26 | NSString *_labelString;
27 | }
28 | @end
29 |
30 | @implementation HackfoldrField
31 |
32 | - (instancetype)init {
33 | if (self = [super init]) {
34 | _subFields = [NSMutableArray array];
35 | }
36 | return self;
37 | }
38 |
39 | - (instancetype)initWithFieldArray:(NSArray *)fields
40 | {
41 | self = [self init];
42 | if (!self) {
43 | return nil;
44 | }
45 |
46 | if (!fields) {
47 | return self;
48 | }
49 |
50 | [fields enumerateObjectsUsingBlock:^(NSString *field, NSUInteger idx, BOOL *stop) {
51 | switch (idx) {
52 | case FieldType_URL:
53 | self.urlString = field;
54 | break;
55 | case FieldType_Title:
56 | self.name = field;
57 | break;
58 | case FieldType_Foldrexpand:
59 | self.actions = field;
60 | break;
61 | case FieldType_Label:
62 | self.labelString = field;
63 | break;
64 | default:
65 | break;
66 | }
67 | }];
68 | // hackfoldr 2.0 rule
69 | self.isCommentLine = [self isCommentLineWithFieldArray:fields];
70 |
71 | return self;
72 | }
73 |
74 | - (BOOL)isCommentLineWithFieldArray:(NSArray *)fields
75 | {
76 | __block BOOL isCommentLine = NO;
77 | [fields enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
78 | if ([obj isKindOfClass:[NSString class]]) {
79 | NSString *field = [obj stringByReplacingOccurrencesOfString:@"\"" withString:@""];;
80 | // only read first one
81 | if ([field hasPrefix:@"#"]) {
82 | isCommentLine = YES;
83 | *stop = YES;
84 | }
85 | }
86 | }];
87 | return isCommentLine;
88 | }
89 |
90 | #pragma mark - Setter and Getter
91 |
92 | - (BOOL)isEmpty
93 | {
94 | if (!self.urlString && !self.name && !self.actions) {
95 | return YES;
96 | }
97 |
98 | if (self.urlString.length == 0 && self.name.length == 0 && self.actions.length == 0) {
99 | return YES;
100 | }
101 |
102 | return NO;
103 | }
104 |
105 | - (void)setUrlString:(NSString *)aURLString
106 | {
107 | NSString *cleanString = [aURLString stringByReplacingOccurrencesOfString:@" " withString:@""];
108 | cleanString = [cleanString stringByReplacingOccurrencesOfString:@"\"" withString:@""];
109 | cleanString = [cleanString stringByReplacingOccurrencesOfString:@"<" withString:@""];
110 | _urlString = cleanString;
111 |
112 | [self setIsSubItemWithURLString:aURLString];
113 | }
114 |
115 | - (NSString *)urlString
116 | {
117 | return _urlString;
118 | }
119 |
120 | - (void)setIsSubItemWithURLString:(NSString *)aURLString
121 | {
122 | // |aURLString| must have '://', otherwise that is not subItem
123 | if (!aURLString ||
124 | aURLString.length == 0 ||
125 | [aURLString rangeOfString:@"://"].location == NSNotFound) {
126 | return;
127 | }
128 |
129 | // hackfoldr 2.0 rule, default is subItem
130 | self.isSubItem = YES;
131 | // While first string is space, this HackfoldrField is subItem
132 | if ([aURLString hasPrefix:@" "]) {
133 | self.isSubItem = YES;
134 | }
135 | if ([aURLString hasPrefix:@"<"]) {
136 | self.isSubItem = NO;
137 | }
138 | }
139 |
140 | - (void)setLabelString:(NSString *)labelString
141 | {
142 | if (labelString.length == 0) {
143 | _labelString = labelString;
144 | return;
145 | }
146 |
147 | __block NSString *colorName = nil;
148 | [[self colorTable] enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull name, UIColor * _Nonnull c, BOOL * _Nonnull stop) {
149 | if ([labelString rangeOfString:name].location != NSNotFound) {
150 | colorName = name;
151 | *stop = YES;
152 | }
153 | }];
154 | if (colorName && [self updateLabelColorByString:colorName]) {
155 | NSString *cleanLabel = [labelString stringByReplacingOccurrencesOfString:colorName withString:@""];
156 | cleanLabel = [cleanLabel stringByReplacingOccurrencesOfString:@"important" withString:@""];
157 | cleanLabel = [cleanLabel stringByReplacingOccurrencesOfString:@"warning" withString:@""];
158 | cleanLabel = [cleanLabel stringByReplacingOccurrencesOfString:@"issue" withString:@""];
159 | cleanLabel = [cleanLabel stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@":"]];
160 | cleanLabel = [cleanLabel stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
161 | _labelString = cleanLabel;
162 | return;
163 | }
164 |
165 | _labelString = labelString;
166 | }
167 |
168 | - (NSString *)labelString
169 | {
170 | return _labelString;
171 | }
172 |
173 | - (NSDictionary *)colorTable {
174 | UIColor *defaultColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.35f];
175 | return @{@"black" : UIColorFromRGB(0x5C6166),
176 | @"blue" : UIColorFromRGB(0x6ECFF5),
177 | @"deep-blue" : UIColorFromRGB(0x006DCC),
178 | @"deep-green" : UIColorFromRGB(0x5BB75B),
179 | @"deep-purple" : UIColorFromRGB(0x564F8A),
180 | @"gray" : defaultColor,
181 | @"green" : UIColorFromRGB(0xA1CF64),
182 | @"important" : UIColorFromRGB(0xD95C5C),
183 | @"issue" : defaultColor,
184 | @"orange" : UIColorFromRGB(0xF0AD4E),
185 | @"pink" : UIColorFromRGB(0xF3A8AA),
186 | @"purple" : UIColorFromRGB(0x9B96F7),
187 | @"red" : UIColorFromRGB(0xD95C5C),
188 | @"teal" : UIColorFromRGB(0x00B5AD),
189 | @"warning" : UIColorFromRGB(0xF0AD4E),
190 | @"yellow" : UIColorFromRGB(0xF0AD4E),
191 | };
192 | }
193 |
194 | - (BOOL)updateLabelColorByString:(NSString *)colorString
195 | {
196 | self.labelColor = [self colorTable][colorString];
197 | if (self.labelColor) {
198 | return YES;
199 | }
200 |
201 | self.labelColor = [self colorTable][@"gray"];
202 | return NO;
203 | }
204 |
205 | #pragma mark - DEBUG
206 |
207 | - (NSString *)description
208 | {
209 | NSMutableString *description = [NSMutableString string];
210 |
211 | [description appendFormat:@"index:%ld ", (long)self.index];
212 | if (self.name) {
213 | [description appendFormat:@"name: %@ ", self.name];
214 | }
215 | if (self.urlString) {
216 | [description appendFormat:@"urlString: %@ ", self.urlString];
217 | }
218 | if (self.actions) {
219 | [description appendFormat:@"actions: %@ ", self.actions];
220 | }
221 |
222 | [description appendFormat:@"isSubItem: %@ ", self.isSubItem ? @"YES" : @"NO"];
223 | [description appendFormat:@"isCommentLine: %@ ", self.isCommentLine ? @"YES" : @"NO"];
224 |
225 | if (self.subFields.count > 0) {
226 | [description appendString:@"subFields: {\n"];
227 | for (HackfoldrField *sf in self.subFields) {
228 | [description appendFormat:@"\t%@\n", sf.description];
229 | }
230 | [description appendString:@"}\n"];
231 | }
232 |
233 | if (self.labelString) {
234 | [description appendFormat:@"labelString: %@ ", self.labelString];
235 | }
236 |
237 | if (self.labelColor) {
238 | [description appendFormat:@"labelColor: %@ ", self.labelColor];
239 | }
240 |
241 | return description;
242 | }
243 |
244 | @end
245 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/SettingViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // SettingViewController.m
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2017/11/2.
6 | // Copyright © 2017年 org.g0v. All rights reserved.
7 | //
8 |
9 | #import "SettingViewController.h"
10 |
11 | #import
12 |
13 | // Model & Client
14 | #import
15 | #import "HackfoldrClient.h"
16 | #import "HackfoldrHistory.h"
17 | #import "HackfoldrPage.h"
18 |
19 | // Cateogry
20 | #import "NSURL+Hackfoldr.h"
21 | #import "NSUserDefaults+DefaultHackfoldrPage.h"
22 |
23 | // View Controller
24 | #import "QRCodeViewController.h"
25 |
26 | @interface SettingViewController ()
27 |
28 | @end
29 |
30 | @implementation SettingViewController
31 |
32 | - (instancetype)init {
33 | XLFormDescriptor *form = [self settingForm];
34 | if (self = [super initWithForm:form]) {
35 | }
36 | return self;
37 | }
38 |
39 | - (void)viewDidLoad {
40 | [super viewDidLoad];
41 |
42 | }
43 |
44 | - (void)viewDidAppear:(BOOL)animated {
45 | [super viewDidAppear:animated];
46 | UIBarButtonItem *shareItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(shareAction)];
47 | [self.navigationController.navigationBar.topItem setRightBarButtonItem:shareItem animated:animated];
48 | }
49 |
50 | - (void)didReceiveMemoryWarning {
51 | [super didReceiveMemoryWarning];
52 | // Dispose of any resources that can be recreated.
53 | }
54 |
55 | - (XLFormDescriptor *)settingForm {
56 | XLFormDescriptor *form = [XLFormDescriptor formDescriptorWithTitle:NSLocalizedStringFromTable(@"Setting Hackfoldr Page", @"Hackfoldr", @"Title of SettingView")];
57 |
58 | XLFormSectionDescriptor *currentPageSection = [XLFormSectionDescriptor formSection];
59 | currentPageSection.title = NSLocalizedStringFromTable(@"Current Hackfoldr", @"Hackfoldr", @"Section title of current hackfoldr");
60 | NSString *key = [[NSUserDefaults standardUserDefaults] stringOfCurrentHackfoldrPage];
61 |
62 | XLFormRowDescriptor *currentHackpageKey = [XLFormRowDescriptor formRowDescriptorWithTag:nil rowType:XLFormRowDescriptorTypeInfo];
63 | currentHackpageKey.title = NSLocalizedStringFromTable(@"Current key", @"Hackfoldr", @"Current key title in SettingView.");
64 | currentHackpageKey.value = key;
65 | [currentPageSection addFormRow:currentHackpageKey];
66 |
67 | XLFormRowDescriptor *currentHackpageDate = [XLFormRowDescriptor formRowDescriptorWithTag:nil rowType:XLFormRowDescriptorTypeDate];
68 | HackfoldrHistory *currentHistory = [HackfoldrHistory MR_findFirstByAttribute:@"hackfoldrKey" withValue:key];
69 | currentHackpageDate.title = NSLocalizedStringFromTable(@"Refresh At", @"Hackfoldr", @"Refresh date about current hackfoldr key in SettingView.");
70 | currentHackpageDate.value = currentHistory.refreshDate;
71 | currentHackpageDate.disabled = @YES;
72 | [currentPageSection addFormRow:currentHackpageDate];
73 |
74 | [form addFormSection:currentPageSection];
75 |
76 | XLFormSectionDescriptor *inputSection = [XLFormSectionDescriptor formSection];
77 | XLFormRowDescriptor *addButton = [XLFormRowDescriptor formRowDescriptorWithTag:nil rowType:XLFormRowDescriptorTypeButton];
78 | addButton.title = NSLocalizedStringFromTable(@"Add Hackfoldr Page", @"Hackfoldr", @"'Add Hackfoldr Page' button title");
79 | __weak XLFormRowDescriptor *wab = addButton;
80 | addButton.action.formBlock = ^(XLFormRowDescriptor * _Nonnull sender) {
81 | void (^deselectCell)(void) = ^() {
82 | UITableViewCell *cell = [sender cellForFormController:self];
83 | NSIndexPath *indexPathOfCell = [self.tableView indexPathForCell:cell];
84 | [self.tableView deselectRowAtIndexPath:indexPathOfCell animated:YES];
85 | };
86 |
87 | __strong XLFormRowDescriptor *sab = wab;
88 | UIAlertController *ac = [UIAlertController alertControllerWithTitle:nil
89 | message:NSLocalizedStringFromTable(@"Select a way to input new key", @"Hackfoldr", @"Message for 'Add Hackfolr Page'")
90 | preferredStyle:UIAlertControllerStyleActionSheet];
91 | [ac addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"Hackfoldr key or URL", @"Hackfoldr", @"Place holder string for input element in SettingView.") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
92 | // Show an alert with textField
93 | UIAlertController *inputAlert = [UIAlertController alertControllerWithTitle:sab.title
94 | message:nil
95 | preferredStyle:UIAlertControllerStyleAlert];
96 | NSString *changeKey = NSLocalizedStringFromTable(@"Change Key", @"Hackfoldr", @"Change hackfoldr key button title in SettingView.");
97 | [inputAlert addAction:[UIAlertAction actionWithTitle:changeKey style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
98 | if (inputAlert.textFields.count == 0) return;
99 |
100 | UITextField *textField = inputAlert.textFields.firstObject;
101 | BFTask *cleanKeyTask = [self validatorHackfoldrKeyForSettingViewWithHackfoldrKey:textField.text];
102 | [cleanKeyTask continueWithBlock:^id(BFTask *task) {
103 | if (task.error) {
104 | self.updateHackfoldrPage(textField.text, task.error);
105 | return nil;
106 | }
107 | if (self.updateHackfoldrPage) {
108 | self.updateHackfoldrPage(task.result, nil);
109 | }
110 | return nil;
111 | }];
112 | }]];
113 | [inputAlert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
114 | textField.placeholder = action.title;
115 | }];
116 | [self presentViewController:inputAlert animated:YES completion:deselectCell];
117 | }]];
118 | [ac addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"Scan QR Code", @"Hackfoldr", @"Scan QR code button title") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
119 | // Show scaner
120 | QRCodeViewController *qvc = [QRCodeViewController viewController];
121 | qvc.foundedResult = ^(NSString *result) {
122 | [self.navigationController popViewControllerAnimated:YES];
123 |
124 | BFTask *cleanKeyTask = [self validatorHackfoldrKeyForSettingViewWithHackfoldrKey:result];
125 | [cleanKeyTask continueWithBlock:^id(BFTask *t) {
126 | if (t.error) {
127 | self.updateHackfoldrPage(t.result, t.error);
128 | return nil;
129 | }
130 | if (self.updateHackfoldrPage) {
131 | self.updateHackfoldrPage(t.result, nil);
132 | }
133 | return nil;
134 | }];
135 | };
136 | [self.navigationController pushViewController:qvc animated:YES];
137 | deselectCell();
138 | }]];
139 | [ac addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"Cancel", @"Hackfoldr", @"Cancel button title of 'Add Hackfoldr Page'") style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
140 | deselectCell();
141 | }]];
142 | [self presentViewController:ac animated:YES completion:nil];
143 | };
144 | [inputSection addFormRow:addButton];
145 |
146 | [form addFormSection:inputSection];
147 |
148 | XLFormSectionDescriptor *historySection = [XLFormSectionDescriptor formSection];
149 | historySection.title = NSLocalizedStringFromTable(@"History", @"Hackfoldr", @"History section title in SettingView");
150 | historySection.footerTitle = NSLocalizedStringFromTable(@"You can select one cell to return to the past.", @"Hackfoldr", @"History section footer in SettingView");
151 | NSArray *histories = [HackfoldrHistory MR_findAllSortedBy:@"refreshDate" ascending:NO];
152 | [histories enumerateObjectsUsingBlock:^(HackfoldrHistory *history, NSUInteger idx, BOOL *stop) {
153 | XLFormRowDescriptor *historyButton = [XLFormRowDescriptor formRowDescriptorWithTag:nil rowType:XLFormRowDescriptorTypeButton];
154 | historyButton.title = history.title;
155 | historyButton.action.formBlock = ^(XLFormRowDescriptor * _Nonnull sender) {
156 | if (self.updateHackfoldrPage) {
157 | self.updateHackfoldrPage(history.hackfoldrKey, nil);
158 | }
159 | };
160 | [historySection addFormRow:historyButton];
161 | }];
162 | [form addFormSection:historySection];
163 |
164 | XLFormSectionDescriptor *resetSection = [XLFormSectionDescriptor formSection];
165 | resetSection.title = NSLocalizedStringFromTable(@"Reset Actions", @"Hackfoldr", @"Reset hackfoldr actions in SettingView");
166 | resetSection.footerTitle = NSLocalizedStringFromTable(@"If you lost in app, just select it.", @"Hackfoldr", @"Reset section footer in SettingView");
167 | XLFormRowDescriptor *restHackfoldrPageButton = [XLFormRowDescriptor formRowDescriptorWithTag:nil rowType:XLFormRowDescriptorTypeButton];
168 | restHackfoldrPageButton.title = NSLocalizedStringFromTable(@"Hackfoldr Help", @"Hackfoldr", @"Reset hackfoldr page button title in SettingView");
169 | restHackfoldrPageButton.action.formBlock = ^(XLFormRowDescriptor * _Nonnull sender) {
170 | // Set current HackfoldrPage to |DefaultHackfoldrPage|
171 | NSString *defaultHackfoldrKey = [[NSUserDefaults standardUserDefaults] stringOfDefaultHackfoldrPage];
172 | if (self.updateHackfoldrPage) {
173 | self.updateHackfoldrPage(defaultHackfoldrKey, nil);
174 | }
175 | };
176 | [resetSection addFormRow:restHackfoldrPageButton];
177 | [form addFormSection:resetSection];
178 |
179 | XLFormSectionDescriptor *infoSection = [XLFormSectionDescriptor formSection];
180 | NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
181 | NSString *build = [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleVersionKey];
182 | infoSection.footerTitle = [NSString stringWithFormat:@"App Version: %@ (%@)\nCreate by Superbil", version, build];
183 | [form addFormSection:infoSection];
184 |
185 | return form;
186 | }
187 |
188 | - (BFTask *)validatorHackfoldrKeyForSettingViewWithHackfoldrKey:(NSString *)newHackfoldrKey
189 | {
190 | BFTaskCompletionSource *completion = [BFTaskCompletionSource taskCompletionSource];
191 |
192 | NSString *validatorKey = [NSURL validatorHackfoldrKey:newHackfoldrKey];
193 |
194 | if (validatorKey && validatorKey.length > 0) {
195 | [completion setResult:validatorKey];
196 | } else {
197 | [completion setError:[NSError errorWithDomain:@"SettingView"
198 | code:1
199 | userInfo:nil]];
200 | }
201 |
202 | return completion.task;
203 | }
204 |
205 | - (void)shareAction {
206 | QRCodeViewController *qrvc = [QRCodeViewController viewController];
207 | NSString *hackfoldrPageKey = [[NSUserDefaults standardUserDefaults] stringOfCurrentHackfoldrPage];
208 | qrvc.qrCodeString = [@"https://hackfoldr.org" stringByAppendingPathComponent:hackfoldrPageKey];
209 | [self.navigationController pushViewController:qrvc animated:YES];
210 | }
211 |
212 | @end
213 |
--------------------------------------------------------------------------------
/hackfoldr-iOS/ListFieldViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ListFieldViewController.m
3 | // hackfoldr-iOS
4 | //
5 | // Created by Superbil on 2014/8/31.
6 | // Copyright (c) 2014年 org.superbil. All rights reserved.
7 | //
8 |
9 | #import "ListFieldViewController.h"
10 |
11 | #import
12 | #import
13 | #import
14 | #import
15 | #import
16 |
17 | // Model & Client
18 | #import
19 | #import "HackfoldrClient.h"
20 | #import "HackfoldrHistory.h"
21 | #import "HackfoldrPage.h"
22 | // Category
23 | #import "HackfoldrClient+Store.h"
24 | #import "HackfoldrPage+CSSearchableItem.h"
25 | #import "NSURL+Hackfoldr.h"
26 | #import "NSUserDefaults+DefaultHackfoldrPage.h"
27 | #import "UIColor+Hackfoldr.h"
28 | #import "UIImage+TOWebViewControllerIcons.h"
29 | // ViewController
30 | #import "ListFieldViewController.h"
31 | #import "SettingViewController.h"
32 | #import "TOWebViewController+HackfoldrField.h"
33 |
34 | @interface ListFieldViewController ()
35 | @property (nonatomic, strong) SettingViewController *settingsController;
36 |
37 | @property (nonatomic, strong) UIRefreshControl *refreshControl;
38 | @property (nonatomic, strong) UIView *refreshLoadingView;
39 | @property (nonatomic, strong) UIImage *hackfoldrImage;
40 | @property (nonatomic, strong) SVGImageView *g0vIcon;
41 | @property (assign) BOOL isRefreshAnimating;
42 | @property (assign) CGAffineTransform defaultIconTransform;
43 | @property (assign) CGFloat lastY;
44 | @end
45 |
46 | @implementation ListFieldViewController
47 |
48 | + (instancetype)viewController
49 | {
50 | return [[[self class] alloc] initWithNibName:nil bundle:nil];
51 | }
52 |
53 | - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
54 | {
55 | if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
56 | _hideBackButton = YES;
57 | }
58 | return self;
59 | }
60 |
61 | - (void)viewDidLoad
62 | {
63 | [super viewDidLoad];
64 |
65 | self.treeView = [[RATreeView alloc] initWithFrame:CGRectZero style:RATreeViewStylePlain];
66 | self.treeView.delegate = self;
67 | self.treeView.dataSource = self;
68 | self.treeView.backgroundColor = [UIColor whiteColor];
69 | [self.view addSubview:self.treeView];
70 | self.treeView.translatesAutoresizingMaskIntoConstraints = NO;
71 | NSLayoutConstraint *topC = [NSLayoutConstraint constraintWithItem:self.treeView
72 | attribute:NSLayoutAttributeTop
73 | relatedBy:NSLayoutRelationEqual
74 | toItem:self.view
75 | attribute:NSLayoutAttributeTop
76 | multiplier:1.0
77 | constant:0.0];
78 | NSLayoutConstraint *leftC = [NSLayoutConstraint constraintWithItem:self.treeView
79 | attribute:NSLayoutAttributeLeft
80 | relatedBy:NSLayoutRelationEqual
81 | toItem:self.view
82 | attribute:NSLayoutAttributeLeft
83 | multiplier:1.0
84 | constant:0.0];
85 | NSLayoutConstraint *rightC = [NSLayoutConstraint constraintWithItem:self.treeView
86 | attribute:NSLayoutAttributeRight
87 | relatedBy:NSLayoutRelationEqual
88 | toItem:self.view
89 | attribute:NSLayoutAttributeRight
90 | multiplier:1.0
91 | constant:0.0];
92 | NSLayoutConstraint *bottomC = [NSLayoutConstraint constraintWithItem:self.treeView
93 | attribute:NSLayoutAttributeBottom
94 | relatedBy:NSLayoutRelationEqual
95 | toItem:self.view
96 | attribute:NSLayoutAttributeBottom
97 | multiplier:1.0
98 | constant:0.0];
99 | [self.view addConstraints:@[topC, leftC, rightC, bottomC]];
100 |
101 | self.settingButton = [UIButton buttonWithType:UIButtonTypeCustom];
102 | [self.settingButton addTarget:self action:@selector(settingAction:) forControlEvents:UIControlEventTouchUpInside];
103 | CGFloat iconSize = 24;
104 | [self.settingButton setImage: [[FAKFontAwesome cogIconWithSize:iconSize] imageWithSize:CGSizeMake(iconSize, iconSize)] forState:UIControlStateNormal];
105 | self.settingButton.frame = CGRectMake(0, 0, iconSize, iconSize);
106 | self.settingButton.accessibilityLabel = @"Settings button";
107 |
108 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:self.settingButton];
109 |
110 | // Hide back button
111 | UIView *leftView = nil;
112 | if (self.hideBackButton) {
113 | leftView = [[UIView alloc] init];
114 | } else {
115 | CGFloat iconSize = 24;
116 | UIImage *image = [[FAKFontAwesome chevronLeftIconWithSize:iconSize] imageWithSize:CGSizeMake(iconSize, iconSize)];
117 | UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
118 | [backButton addTarget:self action:@selector(backAction:) forControlEvents:UIControlEventTouchUpInside];
119 | backButton.frame = CGRectMake(0, 0, iconSize, iconSize);
120 | backButton.accessibilityLabel = @"Back button";
121 | [backButton setImage:image forState:UIControlStateNormal];
122 | leftView = backButton;
123 | }
124 | self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:leftView];
125 |
126 | if (@available(iOS 10.0, *)) {
127 | [self setupRefreshControl];
128 | }
129 | }
130 |
131 | - (void)setupRefreshControl
132 | {
133 | self.refreshControl = [[UIRefreshControl alloc] init];
134 | self.treeView.scrollView.refreshControl = self.refreshControl;
135 |
136 | self.refreshLoadingView = [[UIView alloc] initWithFrame:self.refreshControl.bounds];
137 | self.refreshLoadingView.backgroundColor = [UIColor clearColor];
138 |
139 | NSURL *g0vIconURL = [[NSBundle mainBundle] URLForResource:@"g0v" withExtension:@"svg"];
140 | SVGImageView *icon = [[SVGImageView alloc] initWithContentsOfURL:g0vIconURL];
141 | icon.frame = CGRectMake(0, 0, 24, 24);
142 | self.g0vIcon = icon;
143 | [self.refreshLoadingView addSubview:self.g0vIcon];
144 | self.defaultIconTransform = self.g0vIcon.transform;
145 |
146 | // Clip so the graphics don't stick out
147 | self.refreshLoadingView.clipsToBounds = YES;
148 |
149 | self.refreshControl.tintColor = [UIColor clearColor];
150 |
151 | [self.refreshControl addSubview:self.refreshLoadingView];
152 | [self.refreshControl addTarget:self action:@selector(refreshAction:) forControlEvents:UIControlEventValueChanged];
153 |
154 | self.treeView.scrollView.delegate = self;
155 | }
156 |
157 | - (void)viewWillAppear:(BOOL)animated
158 | {
159 | [super viewWillAppear:animated];
160 |
161 | self.title = self.page.pageTitle;
162 |
163 | [self createSearchIndex];
164 | }
165 |
166 | - (void)viewDidAppear:(BOOL)animated
167 | {
168 | [super viewDidAppear:animated];
169 |
170 | if (self.page.key) {
171 | [[NSUserDefaults standardUserDefaults] setCurrentHackfoldrPage:self.page.key];
172 | [[NSUserDefaults standardUserDefaults] synchronize];
173 | }
174 | }
175 |
176 | - (void)showSettingViewController
177 | {
178 | // When Setting view is showed, don't show again
179 | for (SettingViewController *vc in self.navigationController.viewControllers) {
180 | if ([vc isKindOfClass:[SettingViewController class]]) {
181 | return;
182 | }
183 | }
184 |
185 | SettingViewController *dialogController = [[SettingViewController alloc] init];
186 | self.settingsController = dialogController;
187 | dialogController.updateHackfoldrPage = ^void(NSString * _Nonnull pageKey, NSError * _Nullable error) {
188 | if (error) {
189 | [self.settingsController popoverPresentationController];
190 | } else {
191 | [[self updateHackfoldrPageWithDialogController:self.settingsController key:pageKey] continueWithSuccessBlock:^id _Nullable(BFTask * _Nonnull t) {
192 | [self reloadPage];
193 |
194 | [self.navigationController popViewControllerAnimated:YES];
195 | return nil;
196 | }];
197 | }
198 | };
199 |
200 | if (self.navigationController) {
201 | [self.navigationController pushViewController:dialogController animated:YES];
202 | } else {
203 | UINavigationController *controller = [[UINavigationController alloc] initWithRootViewController:dialogController];
204 | [self presentViewController:controller animated:YES completion:nil];
205 | }
206 | }
207 |
208 | - (BFTask *)updateHackfoldrPageWithDialogController:(SettingViewController *)dialogController key:(NSString *)hackfoldrKey
209 | {
210 | NSString *rediredKey = nil;
211 | // lookup |rediredKey| from core data
212 | HackfoldrHistory *history = [HackfoldrHistory MR_findFirstByAttribute:@"hackfoldrKey" withValue:hackfoldrKey];
213 | if (history && history.rediredKey) {
214 | rediredKey = history.rediredKey;
215 | }
216 |
217 | HackfoldrTaskCompletionSource *completionSource = [[HackfoldrClient sharedClient] hackfoldrPageTaskWithKey:hackfoldrKey rediredKey:rediredKey];
218 |
219 | NSString *title = NSLocalizedStringFromTable(@"Error", @"Hackfoldr", @"Error title");
220 | NSString *dismissButtonTitle = NSLocalizedStringFromTable(@"Dismiss", @"Hackfoldr", @"Dismiss button title in SettingView");
221 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleAlert];
222 |
223 | [alert addAction:[UIAlertAction actionWithTitle:dismissButtonTitle style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
224 | [alert dismissViewControllerAnimated:YES completion:nil];
225 | }]];
226 |
227 | [completionSource.task continueWithBlock:^id _Nullable(BFTask * _Nonnull t) {
228 | if (t.error) {
229 | [self presentViewController:alert animated:YES completion:nil];
230 | }
231 | return nil;
232 | }];
233 |
234 | [[completionSource.task continueWithBlock:^id(BFTask *task) {
235 | return task;
236 | }] continueWithSuccessBlock:^id(BFTask *task) {
237 | NSLog(@"change hackfoldr page to: %@", hackfoldrKey);
238 | // Just save |hackfoldrKey| to user defaults
239 | [[NSUserDefaults standardUserDefaults] setCurrentHackfoldrPage:hackfoldrKey];
240 | [[NSUserDefaults standardUserDefaults] synchronize];
241 |
242 | HackfoldrPage *page = task.result;
243 | self.page = page;
244 | return nil;
245 | }];
246 |
247 | return completionSource.task;
248 | }
249 |
250 | - (void)settingAction:(id)sender
251 | {
252 | [self showSettingViewController];
253 | }
254 |
255 | - (void)backAction:(id)sender
256 | {
257 | [self.navigationController popViewControllerAnimated:YES];
258 | }
259 |
260 | - (void)refreshAction:(id)sender
261 | {
262 | if (!self.page.key) return;
263 |
264 | [[self updateHackfoldrPageWithDialogController:self.settingsController key:self.page.key] continueWithExecutor:[BFExecutor mainThreadExecutor] withBlock:^id _Nullable(BFTask * _Nonnull t) {
265 | [self.refreshControl endRefreshing];
266 |
267 | if (t.error) {
268 | NSLog(@"refresh: %@", t.error);
269 | return nil;
270 | }
271 |
272 | HackfoldrPage *page = t.result;
273 | self.page = page;
274 | [self reloadPage];
275 | return nil;
276 | }];
277 | }
278 |
279 | - (void)updateHackfoldrPageWithKey:(NSString *)hackfoldrKey
280 | {
281 | [[self updateHackfoldrPageWithDialogController:self.settingsController key:hackfoldrKey] continueWithExecutor:[BFExecutor mainThreadExecutor] withBlock:^id _Nullable(BFTask * _Nonnull t) {
282 | [self reloadPage];
283 | return nil;
284 | }];
285 | }
286 |
287 | - (void)reloadPage {
288 | if (!self.page) return;
289 |
290 | [self.treeView reloadData];
291 |
292 | // Just a little delay to workaround UI display
293 | [[BFTask taskWithDelay:100] continueWithExecutor:[BFExecutor mainThreadExecutor] withBlock:^id _Nullable(BFTask * _Nonnull t) {
294 | // Expand field cells
295 | for (HackfoldrField *f in self.page.cells) {
296 | if (f.actions.length > 0) {
297 | // Always expand when |f.actions| had expand
298 | if ([f.actions rangeOfString:@"expand"].location != NSNotFound) {
299 | [self.treeView expandRowForItem:f];
300 | continue;
301 | }
302 | // Always collapse when |f.actions| had collapse
303 | else if ([f.actions rangeOfString:@"collapse"].location != NSNotFound) {
304 | // Do nothing
305 | continue;
306 | }
307 | }
308 |
309 | // Auto expand when |f.subFields| has less than 3 fields
310 | if (f.subFields.count > 0 && f.subFields.count <= 3) {
311 | [self.treeView expandRowForItem:f];
312 | }
313 | }
314 | return nil;
315 | }];
316 | }
317 |
318 | - (void)createSearchIndex {
319 | NSMutableArray *items = [NSMutableArray array];
320 | NSArray *histories = [HackfoldrHistory MR_findAllSortedBy:@"refreshDate" ascending:NO];
321 | [histories enumerateObjectsUsingBlock:^(HackfoldrHistory * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
322 | CSSearchableItem *item = [self.page searchableItemWithDomain:NSStringFromClass(self.class)];
323 | [items addObject:item];
324 | }];
325 |
326 | [CSSearchableIndex.defaultSearchableIndex indexSearchableItems:items completionHandler: ^(NSError * __nullable error) {
327 | if (error) {
328 | NSLog(@"Indexed failed %@", error);
329 | return;
330 | }
331 | NSLog(@"create search indexed");
332 | }];
333 | self.userActivity.eligibleForSearch = YES;
334 | self.userActivity.eligibleForPublicIndexing = YES;
335 | self.userActivity.eligibleForHandoff = YES;
336 |
337 | [self updateUserActivityState:self.userActivity];
338 | }
339 |
340 | - (UIImage *)folderImageWithField:(HackfoldrField *)field
341 | {
342 | if (field.actions && [field.actions rangeOfString:@"expand"].location != NSNotFound) {
343 | return [self folderImageWithisExpand:YES];
344 | }
345 | return [self folderImageWithisExpand:NO];
346 | }
347 |
348 | - (UIImage *)folderImageWithisExpand:(BOOL)isExpand
349 | {
350 | CGFloat iconSize = 24;
351 | UIImage *foldrImage;
352 | if (isExpand) {
353 | foldrImage = [[FAKFontAwesome folderOpenIconWithSize:iconSize] imageWithSize:CGSizeMake(iconSize, iconSize)];
354 | } else {
355 | foldrImage = [[FAKFontAwesome folderIconWithSize:iconSize] imageWithSize:CGSizeMake(iconSize, iconSize)];
356 | }
357 | return foldrImage;
358 | }
359 |
360 | - (UIImage *)hackfoldrImage {
361 | if (_hackfoldrImage) return _hackfoldrImage;
362 |
363 | _hackfoldrImage = [[UIImage imageNamed:@"hackfoldr-icon-small"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
364 | return _hackfoldrImage;
365 | }
366 |
367 | - (void)animateRefreshView
368 | {
369 | self.isRefreshAnimating = YES;
370 |
371 | [UIView animateWithDuration:0.3
372 | delay:0
373 | options:UIViewAnimationOptionCurveLinear
374 | animations:^{
375 | // Rotate the spinner by M_PI_2 = PI/2 = 90 degrees
376 | [self.g0vIcon setTransform:CGAffineTransformRotate(self.g0vIcon.transform, M_PI_2)];
377 | }
378 | completion:^(BOOL finished) {
379 | // If still refreshing, keep spinning, else reset
380 | if (self.refreshControl.isRefreshing) {
381 | [self animateRefreshView];
382 | } else {
383 | self.isRefreshAnimating = NO;
384 | self.g0vIcon.transform = self.defaultIconTransform;
385 | }
386 | }];
387 | }
388 |
389 | #pragma mark - UIScrollViewDelegate
390 |
391 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView
392 | {
393 | // Get the current size of the refresh controller
394 | CGRect refreshBounds = self.refreshControl.bounds;
395 |
396 | // Distance the table has been pulled >= 0
397 | CGFloat pullDistance = MAX(0.0, -self.refreshControl.frame.origin.y);
398 |
399 | // Half the width of the table
400 | CGFloat midX = self.treeView.frame.size.width / 2.0;
401 |
402 | // Calculate the width and height of our graphics
403 | CGFloat iconHeight = self.g0vIcon.bounds.size.height;
404 | CGFloat iconHeightHalf = iconHeight / 2.0;
405 |
406 | CGFloat iconWidth = self.g0vIcon.bounds.size.width;
407 | CGFloat iconWidthHalf = iconWidth / 2.0;
408 |
409 | CGFloat maxPullRange = 100.f;
410 |
411 | // Calculate the pull ratio, between 0.0-1.0
412 | CGFloat pullRatio = MIN( MAX(pullDistance, 0.0), maxPullRange) / maxPullRange;
413 |
414 | // Set the Y coord of the graphics, based on pull distance
415 | CGFloat iconY = pullDistance / 2.0 - iconHeightHalf;
416 |
417 | // Use center to move icon
418 | self.g0vIcon.center = CGPointMake(midX - iconWidthHalf, iconY);
419 |
420 | // Set the encompassing view's frames
421 | refreshBounds.size.height = pullDistance;
422 |
423 | self.refreshLoadingView.frame = refreshBounds;
424 |
425 | if (pullRatio > 0.f) {
426 | CGFloat angle = (-(CGRectGetMaxY(self.refreshControl.frame) - self.lastY) / maxPullRange * 11.25);
427 | self.g0vIcon.transform = CGAffineTransformRotate(self.g0vIcon.transform, angle);
428 | }
429 | self.lastY = CGRectGetMaxY(self.refreshControl.frame);
430 |
431 | // If we're refreshing and the animation is not playing, then play the animation
432 | if (self.refreshControl.isRefreshing && !self.isRefreshAnimating) {
433 | [self animateRefreshView];
434 | }
435 |
436 | NSLog(@"y: %.2f, pullDistance: %.1f, pullRatio: %.3f, iconY: %.2f, isRefreshing: %@",
437 | self.refreshControl.frame.origin.y,
438 | pullDistance,
439 | pullRatio,
440 | iconY,
441 | self.refreshControl.isRefreshing ? @"YES" : @"NO");
442 | }
443 |
444 | #pragma mark - RATreeViewDelegate
445 |
446 | - (void)treeView:(RATreeView *)treeView didSelectRowForItem:(id)item
447 | {
448 | HackfoldrField *rowOfField = item;
449 | if (!rowOfField.isSubItem) {
450 | return;
451 | }
452 |
453 | NSString *urlString = rowOfField.urlString;
454 | NSLog(@"url: %@", urlString);
455 |
456 | if (!urlString || urlString.length == 0) {
457 | // TODO: show nil message
458 | return;
459 | }
460 |
461 | NSURL *targetURL = [NSURL URLWithString:urlString];
462 |
463 | if ([NSURL canHandleHackfoldrURL:targetURL]) {
464 | // Redirect to |urlString|
465 | NSRange range = [urlString rangeOfString:@"://"];
466 | if (range.location != NSNotFound) {
467 | NSString *realKey = [NSURL realKeyOfHackfoldrWithURL:targetURL];
468 | if (!realKey) return;
469 |
470 | [[[HackfoldrClient sharedClient] hackfoldrPageTaskWithKey:realKey rediredKey:nil].task continueWithSuccessBlock:^id _Nullable(BFTask * _Nonnull t) {
471 | HackfoldrPage *page = t.result;
472 |
473 | [[NSUserDefaults standardUserDefaults] setCurrentHackfoldrPage:realKey];
474 |
475 | ListFieldViewController *lvc = [ListFieldViewController viewController];
476 | lvc.hideBackButton = NO;
477 | lvc.page = page;
478 | [self.navigationController pushViewController:lvc animated:YES];
479 | [lvc reloadPage];
480 | return nil;
481 | }];
482 | [treeView deselectRowForItem:item animated:YES];
483 | return;
484 | }
485 | }
486 |
487 | if (NSClassFromString(@"SFSafariViewController")) {
488 | SFSafariViewController *svc = [[SFSafariViewController alloc] initWithURL:targetURL];
489 | svc.title = rowOfField.name;
490 | [self presentViewController:svc animated:YES completion:nil];
491 | } else {
492 | TOWebViewController *webViewController = [[TOWebViewController alloc] init];
493 | webViewController.showPageTitles = YES;
494 | [webViewController loadWithField:rowOfField];
495 |
496 | [self.navigationController pushViewController:webViewController animated:YES];
497 | }
498 | [treeView deselectRowForItem:item animated:YES];
499 | }
500 |
501 | - (BOOL)treeView:(RATreeView *)treeView shouldExpandRowForItem:(id)item
502 | {
503 | if (item) {
504 | NSInteger subIndex = [self.page.cells indexOfObject:item];
505 | return (subIndex != NSNotFound);
506 | }
507 | return YES;
508 | }
509 |
510 | - (void)treeView:(RATreeView *)treeView willExpandRowForItem:(id)item
511 | {
512 | UITableViewCell *cell = [treeView cellForItem:item];
513 | cell.imageView.image = [self folderImageWithisExpand:YES];
514 | }
515 |
516 | - (void)treeView:(RATreeView *)treeView willCollapseRowForItem:(id)item
517 | {
518 | UITableViewCell *cell = [treeView cellForItem:item];
519 | cell.imageView.image = [self folderImageWithisExpand:NO];
520 | }
521 |
522 | #pragma mark - RATreeViewDataSource
523 |
524 | - (NSInteger)treeView:(RATreeView *)treeView numberOfChildrenOfItem:(nullable id)item;
525 | {
526 | if (item) {
527 | NSInteger subIndex = [self.page.cells indexOfObject:item];
528 | HackfoldrField *subField = self.page.cells[subIndex];
529 | return subField.subFields.count;
530 | }
531 |
532 | // Root items
533 | NSUInteger sum = 0;
534 | for (HackfoldrField *f in self.page.cells) {
535 | if (f.index == 0) {
536 | sum += f.subFields.count;
537 | } else if (!f.isSubItem) {
538 | sum += 1;
539 | }
540 | }
541 | return sum;
542 | }
543 |
544 | - (id)treeView:(RATreeView *)treeView child:(NSInteger)index ofItem:(nullable id)item
545 | {
546 | if (item) {
547 | HackfoldrField *field = item;
548 | return field.subFields[index];
549 | }
550 |
551 | HackfoldrField *rootField = nil;
552 | for (HackfoldrField *f in self.page.cells) {
553 | if (f.index == 0) {
554 | rootField = f;
555 | } else {
556 | break;
557 | }
558 | }
559 | if (rootField) {
560 | if (index < rootField.subFields.count) {
561 | return rootField.subFields[index];
562 | }
563 | NSInteger realIndex = index - (rootField.subFields.count - 1);
564 | if (rootField && rootField.subFields.count > 0 && realIndex == 0) {
565 | realIndex += 1;
566 | }
567 | return self.page.cells[realIndex];
568 | }
569 | return self.page.cells[index];
570 | }
571 |
572 | - (UITableViewCell *)treeView:(RATreeView *)treeView cellForItem:(nullable id)item;
573 | {
574 | HackfoldrField *field = item;
575 | NSString *cellName = field.isSubItem ? @"detail" : @"folder";
576 | NSString *identifier = [cellName stringByAppendingString:@"Cell"];
577 | UITableViewCell *cell = [treeView dequeueReusableCellWithIdentifier:identifier];
578 | if (!cell) {
579 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];
580 | }
581 |
582 | // Default color is white
583 | cell.backgroundColor = [UIColor whiteColor];
584 | cell.detailTextLabel.backgroundColor = [UIColor whiteColor];
585 |
586 | CGFloat fontSize = [UIFont systemFontSize];
587 |
588 | if (field.isSubItem) {
589 | UIImage *iconImage = nil;
590 | CGFloat iconSize = [UIFont systemFontSize] + 2.f;
591 |
592 | if ([NSURL canHandleHackfoldrURL:[NSURL URLWithString:field.urlString]]) {
593 | iconImage = self.hackfoldrImage;
594 | } else if (field.actions.length > 0 && [field.actions rangeOfString:@"fa-"].location != NSNotFound) {
595 | // Custom icon
596 | NSString *iconName = nil;
597 | NSArray *actions = [field.actions componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@","]];
598 | for (NSString *s in actions) {
599 | if ([s rangeOfString:@"fa-"].location != NSNotFound) {
600 | iconName = [s stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
601 | break;
602 | }
603 | }
604 |
605 | NSError *error = nil;
606 | FAKFontAwesome *fa = [FAKFontAwesome iconWithIdentifier:iconName size:iconSize error:&error];
607 | if (!error) {
608 | iconImage = [fa imageWithSize:CGSizeMake(iconSize, iconSize)];
609 | } else {
610 | NSLog(@"FontAwsome %@, %@", iconName, error);
611 | }
612 | }
613 |
614 | if (iconImage) {
615 | UIImageView *iconView = [[UIImageView alloc] initWithImage:iconImage];
616 | iconView.frame = CGRectMake(0, 0, iconSize, iconSize);
617 | iconView.tintColor = [UIColor hackfoldrGreenColor];
618 | cell.accessoryView = iconView;
619 | } else {
620 | cell.accessoryType = field.urlString.length > 0 ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone;
621 | cell.accessoryView = nil;
622 | }
623 |
624 | // Only setup when |field.labelString| have value
625 | NSString *cleanLabelString = [field.labelString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
626 | if (cleanLabelString.length > 0) {
627 | cell.detailTextLabel.text = [NSString stringWithFormat:@" %@ ", field.labelString];
628 | cell.detailTextLabel.textColor = [UIColor whiteColor];
629 | cell.detailTextLabel.backgroundColor = field.labelColor;
630 | [cell.detailTextLabel.layer setCornerRadius:3.f];
631 | [cell.detailTextLabel.layer setMasksToBounds:YES];
632 | } else {
633 | cell.detailTextLabel.text = nil;
634 | }
635 | } else {
636 | cell.detailTextLabel.text = nil;
637 | fontSize += 4.f;
638 |
639 | cell.imageView.image = [self folderImageWithField:field];
640 | }
641 |
642 | cell.textLabel.text = field.name;
643 | cell.textLabel.font = [UIFont systemFontOfSize:fontSize];
644 |
645 | NSLog(@"field:%@", field);
646 |
647 | return cell;
648 | }
649 |
650 | @end
651 |
--------------------------------------------------------------------------------
/hackfoldr-iOS.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 3AB4D0070023B1FA82E42FAE /* libPods-hackfoldr-iOSTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 893ECEAB2C1FEB82EE9E1ED6 /* libPods-hackfoldr-iOSTests.a */; };
11 | 882E652A19B2D5920054E522 /* ListFieldViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 882E652919B2D5920054E522 /* ListFieldViewController.m */; };
12 | 886F4A471E0536AA00AF14DE /* NSURL+Hackfoldr.m in Sources */ = {isa = PBXBuildFile; fileRef = 886F4A461E0536AA00AF14DE /* NSURL+Hackfoldr.m */; };
13 | 88AC6BEA1F6BD13000E082E2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 88AC6BE91F6BD13000E082E2 /* LaunchScreen.storyboard */; };
14 | D7BD8128463F908AE0D361C0 /* libPods-hackfoldr-iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E2BE931B1EC9D9EDF8673F5C /* libPods-hackfoldr-iOS.a */; };
15 | E70F906D1A77F72200BF4B25 /* NSUserDefaults+DefaultHackfoldrPage.m in Sources */ = {isa = PBXBuildFile; fileRef = E70F906C1A77F72200BF4B25 /* NSUserDefaults+DefaultHackfoldrPage.m */; };
16 | E71D192A1956CE5500B57207 /* HackfoldrClient.m in Sources */ = {isa = PBXBuildFile; fileRef = E71D19291956CE5500B57207 /* HackfoldrClient.m */; };
17 | E71D192D1956CFB600B57207 /* HackfoldrField.m in Sources */ = {isa = PBXBuildFile; fileRef = E71D192C1956CFB600B57207 /* HackfoldrField.m */; };
18 | E755907A1AEAB595007337B1 /* Hackfoldr.strings in Resources */ = {isa = PBXBuildFile; fileRef = E75590781AEAB595007337B1 /* Hackfoldr.strings */; };
19 | E77B93D11FAB1F4800BD8130 /* HackfoldrPage+CSSearchableItem.m in Sources */ = {isa = PBXBuildFile; fileRef = E77B93D01FAB1F4800BD8130 /* HackfoldrPage+CSSearchableItem.m */; };
20 | E77B93D41FAB29EC00BD8130 /* SettingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E77B93D31FAB29EC00BD8130 /* SettingViewController.m */; };
21 | E79424AE1ACEEDCF00558AE9 /* HackoldrModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = E79424AC1ACEEDCF00558AE9 /* HackoldrModel.xcdatamodeld */; };
22 | E79424B11ACEEF6400558AE9 /* HackfoldrHistory.m in Sources */ = {isa = PBXBuildFile; fileRef = E79424B01ACEEF6400558AE9 /* HackfoldrHistory.m */; };
23 | E79424B31ACF015300558AE9 /* sample.csv.json in Resources */ = {isa = PBXBuildFile; fileRef = E79424B21ACF015300558AE9 /* sample.csv.json */; };
24 | E79937391FC4C00C00DAC153 /* QRCodeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E79937381FC4C00C00DAC153 /* QRCodeViewController.m */; };
25 | E799373B1FC4C71000DAC153 /* QRCode.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E799373A1FC4C71000DAC153 /* QRCode.storyboard */; };
26 | E79B0E771E7AE43F005BE38C /* UIColor+Hackfoldr.m in Sources */ = {isa = PBXBuildFile; fileRef = E79B0E761E7AE43F005BE38C /* UIColor+Hackfoldr.m */; };
27 | E79B0E7A1E7B1714005BE38C /* HackfoldrClient+Store.m in Sources */ = {isa = PBXBuildFile; fileRef = E79B0E791E7B1714005BE38C /* HackfoldrClient+Store.m */; };
28 | E79B0E7E1E7C030E005BE38C /* g0v.svg in Resources */ = {isa = PBXBuildFile; fileRef = E79B0E7D1E7C030E005BE38C /* g0v.svg */; };
29 | E7B1DD8A195AEE7D00768FFA /* TOWebViewController+HackfoldrField.m in Sources */ = {isa = PBXBuildFile; fileRef = E7B1DD89195AEE7D00768FFA /* TOWebViewController+HackfoldrField.m */; };
30 | E7EB66BC19554E290069FD71 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7EB66BB19554E290069FD71 /* Foundation.framework */; };
31 | E7EB66BE19554E290069FD71 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7EB66BD19554E290069FD71 /* CoreGraphics.framework */; };
32 | E7EB66C019554E290069FD71 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7EB66BF19554E290069FD71 /* UIKit.framework */; };
33 | E7EB66C619554E290069FD71 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = E7EB66C419554E290069FD71 /* InfoPlist.strings */; };
34 | E7EB66C819554E290069FD71 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E7EB66C719554E290069FD71 /* main.m */; };
35 | E7EB66CC19554E290069FD71 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E7EB66CB19554E290069FD71 /* AppDelegate.m */; };
36 | E7EB66D519554E290069FD71 /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E7EB66D419554E290069FD71 /* MainViewController.m */; };
37 | E7EB66D719554E290069FD71 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E7EB66D619554E290069FD71 /* Images.xcassets */; };
38 | E7EB66DE19554E290069FD71 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7EB66DD19554E290069FD71 /* XCTest.framework */; };
39 | E7EB66DF19554E290069FD71 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7EB66BB19554E290069FD71 /* Foundation.framework */; };
40 | E7EB66E019554E290069FD71 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7EB66BF19554E290069FD71 /* UIKit.framework */; };
41 | E7EB66E819554E290069FD71 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = E7EB66E619554E290069FD71 /* InfoPlist.strings */; };
42 | E7EB66EA19554E290069FD71 /* hackfoldr_iOSTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E7EB66E919554E290069FD71 /* hackfoldr_iOSTests.m */; };
43 | E7F7482A1955878C00262A34 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7F748291955878C00262A34 /* QuartzCore.framework */; };
44 | E7F9558719570D4E0098661E /* HackfoldrPage.m in Sources */ = {isa = PBXBuildFile; fileRef = E7F9558619570D4E0098661E /* HackfoldrPage.m */; };
45 | /* End PBXBuildFile section */
46 |
47 | /* Begin PBXContainerItemProxy section */
48 | E7EB66E119554E290069FD71 /* PBXContainerItemProxy */ = {
49 | isa = PBXContainerItemProxy;
50 | containerPortal = E7EB66B019554E290069FD71 /* Project object */;
51 | proxyType = 1;
52 | remoteGlobalIDString = E7EB66B719554E290069FD71;
53 | remoteInfo = "hackfoldr-iOS";
54 | };
55 | /* End PBXContainerItemProxy section */
56 |
57 | /* Begin PBXFileReference section */
58 | 0FFCE6D7B8522BC376B03A51 /* Pods-hackfoldr-iOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-hackfoldr-iOSTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-hackfoldr-iOSTests/Pods-hackfoldr-iOSTests.release.xcconfig"; sourceTree = ""; };
59 | 70B8275F56498EFBD8F1410F /* Pods-hackfoldr-iOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-hackfoldr-iOSTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-hackfoldr-iOSTests/Pods-hackfoldr-iOSTests.debug.xcconfig"; sourceTree = ""; };
60 | 882E652819B2D5920054E522 /* ListFieldViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ListFieldViewController.h; path = "hackfoldr-iOS/ListFieldViewController.h"; sourceTree = ""; };
61 | 882E652919B2D5920054E522 /* ListFieldViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ListFieldViewController.m; path = "hackfoldr-iOS/ListFieldViewController.m"; sourceTree = ""; };
62 | 886F4A451E0536AA00AF14DE /* NSURL+Hackfoldr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSURL+Hackfoldr.h"; path = "hackfoldr-iOS/NSURL+Hackfoldr.h"; sourceTree = ""; };
63 | 886F4A461E0536AA00AF14DE /* NSURL+Hackfoldr.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSURL+Hackfoldr.m"; path = "hackfoldr-iOS/NSURL+Hackfoldr.m"; sourceTree = ""; };
64 | 889DFBC61D28EE740066ECA2 /* hackfoldr-iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "hackfoldr-iOS.entitlements"; sourceTree = ""; };
65 | 88AC6BE91F6BD13000E082E2 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; };
66 | 893ECEAB2C1FEB82EE9E1ED6 /* libPods-hackfoldr-iOSTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-hackfoldr-iOSTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
67 | D6F8B8E84166B0CC5D012432 /* Pods-hackfoldr-iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-hackfoldr-iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-hackfoldr-iOS/Pods-hackfoldr-iOS.debug.xcconfig"; sourceTree = ""; };
68 | E2BE931B1EC9D9EDF8673F5C /* libPods-hackfoldr-iOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-hackfoldr-iOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
69 | E70F906B1A77F72200BF4B25 /* NSUserDefaults+DefaultHackfoldrPage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSUserDefaults+DefaultHackfoldrPage.h"; path = "hackfoldr-iOS/NSUserDefaults+DefaultHackfoldrPage.h"; sourceTree = ""; };
70 | E70F906C1A77F72200BF4B25 /* NSUserDefaults+DefaultHackfoldrPage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSUserDefaults+DefaultHackfoldrPage.m"; path = "hackfoldr-iOS/NSUserDefaults+DefaultHackfoldrPage.m"; sourceTree = ""; };
71 | E71D19281956CE5500B57207 /* HackfoldrClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HackfoldrClient.h; path = "hackfoldr-iOS/HackfoldrClient.h"; sourceTree = ""; };
72 | E71D19291956CE5500B57207 /* HackfoldrClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HackfoldrClient.m; path = "hackfoldr-iOS/HackfoldrClient.m"; sourceTree = ""; };
73 | E71D192B1956CFB600B57207 /* HackfoldrField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HackfoldrField.h; path = "hackfoldr-iOS/HackfoldrField.h"; sourceTree = ""; };
74 | E71D192C1956CFB600B57207 /* HackfoldrField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HackfoldrField.m; path = "hackfoldr-iOS/HackfoldrField.m"; sourceTree = ""; };
75 | E75590791AEAB595007337B1 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Hackfoldr.strings"; sourceTree = ""; };
76 | E755907B1AEAB5A0007337B1 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Hackfoldr.strings; sourceTree = ""; };
77 | E77B93CF1FAB1F4800BD8130 /* HackfoldrPage+CSSearchableItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "HackfoldrPage+CSSearchableItem.h"; path = "hackfoldr-iOS/HackfoldrPage+CSSearchableItem.h"; sourceTree = ""; };
78 | E77B93D01FAB1F4800BD8130 /* HackfoldrPage+CSSearchableItem.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "HackfoldrPage+CSSearchableItem.m"; path = "hackfoldr-iOS/HackfoldrPage+CSSearchableItem.m"; sourceTree = ""; };
79 | E77B93D21FAB29EC00BD8130 /* SettingViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SettingViewController.h; path = "hackfoldr-iOS/SettingViewController.h"; sourceTree = ""; };
80 | E77B93D31FAB29EC00BD8130 /* SettingViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SettingViewController.m; path = "hackfoldr-iOS/SettingViewController.m"; sourceTree = ""; };
81 | E79424AD1ACEEDCF00558AE9 /* HackoldrModel.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = HackoldrModel.xcdatamodel; sourceTree = ""; };
82 | E79424AF1ACEEF6400558AE9 /* HackfoldrHistory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HackfoldrHistory.h; path = "hackfoldr-iOS/HackfoldrHistory.h"; sourceTree = ""; };
83 | E79424B01ACEEF6400558AE9 /* HackfoldrHistory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HackfoldrHistory.m; path = "hackfoldr-iOS/HackfoldrHistory.m"; sourceTree = ""; };
84 | E79424B21ACF015300558AE9 /* sample.csv.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = sample.csv.json; path = TestData/sample.csv.json; sourceTree = ""; };
85 | E79937371FC4C00C00DAC153 /* QRCodeViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = QRCodeViewController.h; path = "hackfoldr-iOS/QRCodeViewController.h"; sourceTree = ""; };
86 | E79937381FC4C00C00DAC153 /* QRCodeViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = QRCodeViewController.m; path = "hackfoldr-iOS/QRCodeViewController.m"; sourceTree = ""; };
87 | E799373A1FC4C71000DAC153 /* QRCode.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = QRCode.storyboard; path = "hackfoldr-iOS/QRCode.storyboard"; sourceTree = ""; };
88 | E79B0E751E7AE43F005BE38C /* UIColor+Hackfoldr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIColor+Hackfoldr.h"; path = "hackfoldr-iOS/UIColor+Hackfoldr.h"; sourceTree = ""; };
89 | E79B0E761E7AE43F005BE38C /* UIColor+Hackfoldr.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIColor+Hackfoldr.m"; path = "hackfoldr-iOS/UIColor+Hackfoldr.m"; sourceTree = ""; };
90 | E79B0E781E7B1714005BE38C /* HackfoldrClient+Store.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "HackfoldrClient+Store.h"; path = "hackfoldr-iOS/HackfoldrClient+Store.h"; sourceTree = ""; };
91 | E79B0E791E7B1714005BE38C /* HackfoldrClient+Store.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "HackfoldrClient+Store.m"; path = "hackfoldr-iOS/HackfoldrClient+Store.m"; sourceTree = ""; };
92 | E79B0E7D1E7C030E005BE38C /* g0v.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = g0v.svg; sourceTree = ""; };
93 | E7A644D31FCEC12D00D28263 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/InfoPlist.strings"; sourceTree = ""; };
94 | E7B1DD88195AEE7D00768FFA /* TOWebViewController+HackfoldrField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "TOWebViewController+HackfoldrField.h"; path = "hackfoldr-iOS/TOWebViewController+HackfoldrField.h"; sourceTree = ""; };
95 | E7B1DD89195AEE7D00768FFA /* TOWebViewController+HackfoldrField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "TOWebViewController+HackfoldrField.m"; path = "hackfoldr-iOS/TOWebViewController+HackfoldrField.m"; sourceTree = ""; };
96 | E7EB66B819554E290069FD71 /* hackfoldr-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "hackfoldr-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
97 | E7EB66BB19554E290069FD71 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
98 | E7EB66BD19554E290069FD71 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
99 | E7EB66BF19554E290069FD71 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
100 | E7EB66C319554E290069FD71 /* hackfoldr-iOS-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "hackfoldr-iOS-Info.plist"; sourceTree = ""; };
101 | E7EB66C519554E290069FD71 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
102 | E7EB66C719554E290069FD71 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
103 | E7EB66C919554E290069FD71 /* hackfoldr-iOS-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "hackfoldr-iOS-Prefix.pch"; sourceTree = ""; };
104 | E7EB66CA19554E290069FD71 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
105 | E7EB66CB19554E290069FD71 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
106 | E7EB66D319554E290069FD71 /* MainViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MainViewController.h; path = "hackfoldr-iOS/MainViewController.h"; sourceTree = ""; };
107 | E7EB66D419554E290069FD71 /* MainViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = MainViewController.m; path = "hackfoldr-iOS/MainViewController.m"; sourceTree = ""; };
108 | E7EB66D619554E290069FD71 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
109 | E7EB66DC19554E290069FD71 /* hackfoldr-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "hackfoldr-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
110 | E7EB66DD19554E290069FD71 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
111 | E7EB66E519554E290069FD71 /* hackfoldr-iOSTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "hackfoldr-iOSTests-Info.plist"; sourceTree = ""; };
112 | E7EB66E719554E290069FD71 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
113 | E7EB66E919554E290069FD71 /* hackfoldr_iOSTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = hackfoldr_iOSTests.m; sourceTree = ""; };
114 | E7F748291955878C00262A34 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
115 | E7F9558519570D4E0098661E /* HackfoldrPage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HackfoldrPage.h; path = "hackfoldr-iOS/HackfoldrPage.h"; sourceTree = ""; };
116 | E7F9558619570D4E0098661E /* HackfoldrPage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HackfoldrPage.m; path = "hackfoldr-iOS/HackfoldrPage.m"; sourceTree = ""; };
117 | F15476DD2E850E7A93F2DF39 /* Pods-hackfoldr-iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-hackfoldr-iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-hackfoldr-iOS/Pods-hackfoldr-iOS.release.xcconfig"; sourceTree = ""; };
118 | /* End PBXFileReference section */
119 |
120 | /* Begin PBXFrameworksBuildPhase section */
121 | E7EB66B519554E290069FD71 /* Frameworks */ = {
122 | isa = PBXFrameworksBuildPhase;
123 | buildActionMask = 2147483647;
124 | files = (
125 | E7F7482A1955878C00262A34 /* QuartzCore.framework in Frameworks */,
126 | E7EB66BE19554E290069FD71 /* CoreGraphics.framework in Frameworks */,
127 | E7EB66C019554E290069FD71 /* UIKit.framework in Frameworks */,
128 | E7EB66BC19554E290069FD71 /* Foundation.framework in Frameworks */,
129 | D7BD8128463F908AE0D361C0 /* libPods-hackfoldr-iOS.a in Frameworks */,
130 | );
131 | runOnlyForDeploymentPostprocessing = 0;
132 | };
133 | E7EB66D919554E290069FD71 /* Frameworks */ = {
134 | isa = PBXFrameworksBuildPhase;
135 | buildActionMask = 2147483647;
136 | files = (
137 | E7EB66DE19554E290069FD71 /* XCTest.framework in Frameworks */,
138 | E7EB66E019554E290069FD71 /* UIKit.framework in Frameworks */,
139 | E7EB66DF19554E290069FD71 /* Foundation.framework in Frameworks */,
140 | 3AB4D0070023B1FA82E42FAE /* libPods-hackfoldr-iOSTests.a in Frameworks */,
141 | );
142 | runOnlyForDeploymentPostprocessing = 0;
143 | };
144 | /* End PBXFrameworksBuildPhase section */
145 |
146 | /* Begin PBXGroup section */
147 | 53725B8A749867B92203B128 /* Pods */ = {
148 | isa = PBXGroup;
149 | children = (
150 | D6F8B8E84166B0CC5D012432 /* Pods-hackfoldr-iOS.debug.xcconfig */,
151 | F15476DD2E850E7A93F2DF39 /* Pods-hackfoldr-iOS.release.xcconfig */,
152 | 70B8275F56498EFBD8F1410F /* Pods-hackfoldr-iOSTests.debug.xcconfig */,
153 | 0FFCE6D7B8522BC376B03A51 /* Pods-hackfoldr-iOSTests.release.xcconfig */,
154 | );
155 | name = Pods;
156 | sourceTree = "";
157 | };
158 | E70F906E1A77F7A300BF4B25 /* Extensions */ = {
159 | isa = PBXGroup;
160 | children = (
161 | E79B0E781E7B1714005BE38C /* HackfoldrClient+Store.h */,
162 | E79B0E791E7B1714005BE38C /* HackfoldrClient+Store.m */,
163 | E77B93CF1FAB1F4800BD8130 /* HackfoldrPage+CSSearchableItem.h */,
164 | E77B93D01FAB1F4800BD8130 /* HackfoldrPage+CSSearchableItem.m */,
165 | 886F4A451E0536AA00AF14DE /* NSURL+Hackfoldr.h */,
166 | 886F4A461E0536AA00AF14DE /* NSURL+Hackfoldr.m */,
167 | E70F906B1A77F72200BF4B25 /* NSUserDefaults+DefaultHackfoldrPage.h */,
168 | E70F906C1A77F72200BF4B25 /* NSUserDefaults+DefaultHackfoldrPage.m */,
169 | E79B0E751E7AE43F005BE38C /* UIColor+Hackfoldr.h */,
170 | E79B0E761E7AE43F005BE38C /* UIColor+Hackfoldr.m */,
171 | );
172 | name = Extensions;
173 | sourceTree = "";
174 | };
175 | E71D192E1956DAF800B57207 /* Models */ = {
176 | isa = PBXGroup;
177 | children = (
178 | E71D192B1956CFB600B57207 /* HackfoldrField.h */,
179 | E71D192C1956CFB600B57207 /* HackfoldrField.m */,
180 | E79424AF1ACEEF6400558AE9 /* HackfoldrHistory.h */,
181 | E79424B01ACEEF6400558AE9 /* HackfoldrHistory.m */,
182 | E7F9558519570D4E0098661E /* HackfoldrPage.h */,
183 | E7F9558619570D4E0098661E /* HackfoldrPage.m */,
184 | E79424AC1ACEEDCF00558AE9 /* HackoldrModel.xcdatamodeld */,
185 | );
186 | name = Models;
187 | sourceTree = "";
188 | };
189 | E71D192F1956DB0300B57207 /* Client */ = {
190 | isa = PBXGroup;
191 | children = (
192 | E71D19281956CE5500B57207 /* HackfoldrClient.h */,
193 | E71D19291956CE5500B57207 /* HackfoldrClient.m */,
194 | );
195 | name = Client;
196 | sourceTree = "";
197 | };
198 | E7B1DD871959AFE700768FFA /* ViewController */ = {
199 | isa = PBXGroup;
200 | children = (
201 | 882E652819B2D5920054E522 /* ListFieldViewController.h */,
202 | 882E652919B2D5920054E522 /* ListFieldViewController.m */,
203 | E7EB66D319554E290069FD71 /* MainViewController.h */,
204 | E7EB66D419554E290069FD71 /* MainViewController.m */,
205 | E799373A1FC4C71000DAC153 /* QRCode.storyboard */,
206 | E79937371FC4C00C00DAC153 /* QRCodeViewController.h */,
207 | E79937381FC4C00C00DAC153 /* QRCodeViewController.m */,
208 | E77B93D21FAB29EC00BD8130 /* SettingViewController.h */,
209 | E77B93D31FAB29EC00BD8130 /* SettingViewController.m */,
210 | E7B1DD88195AEE7D00768FFA /* TOWebViewController+HackfoldrField.h */,
211 | E7B1DD89195AEE7D00768FFA /* TOWebViewController+HackfoldrField.m */,
212 | );
213 | name = ViewController;
214 | sourceTree = "";
215 | };
216 | E7EB66AF19554E290069FD71 = {
217 | isa = PBXGroup;
218 | children = (
219 | E7B1DD871959AFE700768FFA /* ViewController */,
220 | E71D192F1956DB0300B57207 /* Client */,
221 | E70F906E1A77F7A300BF4B25 /* Extensions */,
222 | E71D192E1956DAF800B57207 /* Models */,
223 | E7EB66BA19554E290069FD71 /* Frameworks */,
224 | E7EB66C119554E290069FD71 /* hackfoldr-iOS */,
225 | E7EB66E319554E290069FD71 /* hackfoldr-iOSTests */,
226 | E7EB66B919554E290069FD71 /* Products */,
227 | 53725B8A749867B92203B128 /* Pods */,
228 | );
229 | sourceTree = "";
230 | };
231 | E7EB66B919554E290069FD71 /* Products */ = {
232 | isa = PBXGroup;
233 | children = (
234 | E7EB66B819554E290069FD71 /* hackfoldr-iOS.app */,
235 | E7EB66DC19554E290069FD71 /* hackfoldr-iOSTests.xctest */,
236 | );
237 | name = Products;
238 | sourceTree = "";
239 | };
240 | E7EB66BA19554E290069FD71 /* Frameworks */ = {
241 | isa = PBXGroup;
242 | children = (
243 | E7F748291955878C00262A34 /* QuartzCore.framework */,
244 | E7EB66BB19554E290069FD71 /* Foundation.framework */,
245 | E7EB66BD19554E290069FD71 /* CoreGraphics.framework */,
246 | E7EB66BF19554E290069FD71 /* UIKit.framework */,
247 | E7EB66DD19554E290069FD71 /* XCTest.framework */,
248 | E2BE931B1EC9D9EDF8673F5C /* libPods-hackfoldr-iOS.a */,
249 | 893ECEAB2C1FEB82EE9E1ED6 /* libPods-hackfoldr-iOSTests.a */,
250 | );
251 | name = Frameworks;
252 | sourceTree = "";
253 | };
254 | E7EB66C119554E290069FD71 /* hackfoldr-iOS */ = {
255 | isa = PBXGroup;
256 | children = (
257 | 889DFBC61D28EE740066ECA2 /* hackfoldr-iOS.entitlements */,
258 | E7EB66CA19554E290069FD71 /* AppDelegate.h */,
259 | E7EB66CB19554E290069FD71 /* AppDelegate.m */,
260 | E7EB66D619554E290069FD71 /* Images.xcassets */,
261 | E79B0E7D1E7C030E005BE38C /* g0v.svg */,
262 | 88AC6BE91F6BD13000E082E2 /* LaunchScreen.storyboard */,
263 | E7EB66C219554E290069FD71 /* Supporting Files */,
264 | );
265 | path = "hackfoldr-iOS";
266 | sourceTree = "";
267 | };
268 | E7EB66C219554E290069FD71 /* Supporting Files */ = {
269 | isa = PBXGroup;
270 | children = (
271 | E7EB66C319554E290069FD71 /* hackfoldr-iOS-Info.plist */,
272 | E7EB66C919554E290069FD71 /* hackfoldr-iOS-Prefix.pch */,
273 | E75590781AEAB595007337B1 /* Hackfoldr.strings */,
274 | E7EB66C419554E290069FD71 /* InfoPlist.strings */,
275 | E7EB66C719554E290069FD71 /* main.m */,
276 | );
277 | name = "Supporting Files";
278 | sourceTree = "";
279 | };
280 | E7EB66E319554E290069FD71 /* hackfoldr-iOSTests */ = {
281 | isa = PBXGroup;
282 | children = (
283 | E79424B21ACF015300558AE9 /* sample.csv.json */,
284 | E7EB66E919554E290069FD71 /* hackfoldr_iOSTests.m */,
285 | E7EB66E419554E290069FD71 /* Supporting Files */,
286 | );
287 | path = "hackfoldr-iOSTests";
288 | sourceTree = "";
289 | };
290 | E7EB66E419554E290069FD71 /* Supporting Files */ = {
291 | isa = PBXGroup;
292 | children = (
293 | E7EB66E519554E290069FD71 /* hackfoldr-iOSTests-Info.plist */,
294 | E7EB66E619554E290069FD71 /* InfoPlist.strings */,
295 | );
296 | name = "Supporting Files";
297 | sourceTree = "";
298 | };
299 | /* End PBXGroup section */
300 |
301 | /* Begin PBXNativeTarget section */
302 | E7EB66B719554E290069FD71 /* hackfoldr-iOS */ = {
303 | isa = PBXNativeTarget;
304 | buildConfigurationList = E7EB66ED19554E290069FD71 /* Build configuration list for PBXNativeTarget "hackfoldr-iOS" */;
305 | buildPhases = (
306 | 052512BDB754F2B582BE1463 /* [CP] Check Pods Manifest.lock */,
307 | E7EB66B419554E290069FD71 /* Sources */,
308 | E7EB66B519554E290069FD71 /* Frameworks */,
309 | E7EB66B619554E290069FD71 /* Resources */,
310 | E7118A521AD4FFB7007608C0 /* Update app bundle version */,
311 | 69F766257D99B6A5FCB74096 /* [CP] Copy Pods Resources */,
312 | );
313 | buildRules = (
314 | );
315 | dependencies = (
316 | );
317 | name = "hackfoldr-iOS";
318 | productName = "hackfoldr-iOS";
319 | productReference = E7EB66B819554E290069FD71 /* hackfoldr-iOS.app */;
320 | productType = "com.apple.product-type.application";
321 | };
322 | E7EB66DB19554E290069FD71 /* hackfoldr-iOSTests */ = {
323 | isa = PBXNativeTarget;
324 | buildConfigurationList = E7EB66F019554E290069FD71 /* Build configuration list for PBXNativeTarget "hackfoldr-iOSTests" */;
325 | buildPhases = (
326 | 77FF7C2AC3EDECEA679C69E0 /* [CP] Check Pods Manifest.lock */,
327 | E7EB66D819554E290069FD71 /* Sources */,
328 | E7EB66D919554E290069FD71 /* Frameworks */,
329 | E7EB66DA19554E290069FD71 /* Resources */,
330 | );
331 | buildRules = (
332 | );
333 | dependencies = (
334 | E7EB66E219554E290069FD71 /* PBXTargetDependency */,
335 | );
336 | name = "hackfoldr-iOSTests";
337 | productName = "hackfoldr-iOSTests";
338 | productReference = E7EB66DC19554E290069FD71 /* hackfoldr-iOSTests.xctest */;
339 | productType = "com.apple.product-type.bundle.unit-test";
340 | };
341 | /* End PBXNativeTarget section */
342 |
343 | /* Begin PBXProject section */
344 | E7EB66B019554E290069FD71 /* Project object */ = {
345 | isa = PBXProject;
346 | attributes = {
347 | LastUpgradeCheck = 1010;
348 | ORGANIZATIONNAME = org.g0v;
349 | TargetAttributes = {
350 | E7EB66B719554E290069FD71 = {
351 | DevelopmentTeam = 67Y6DS58YP;
352 | SystemCapabilities = {
353 | com.apple.SafariKeychain = {
354 | enabled = 1;
355 | };
356 | };
357 | };
358 | E7EB66DB19554E290069FD71 = {
359 | TestTargetID = E7EB66B719554E290069FD71;
360 | };
361 | };
362 | };
363 | buildConfigurationList = E7EB66B319554E290069FD71 /* Build configuration list for PBXProject "hackfoldr-iOS" */;
364 | compatibilityVersion = "Xcode 3.2";
365 | developmentRegion = English;
366 | hasScannedForEncodings = 0;
367 | knownRegions = (
368 | English,
369 | en,
370 | Base,
371 | "zh-Hant",
372 | );
373 | mainGroup = E7EB66AF19554E290069FD71;
374 | productRefGroup = E7EB66B919554E290069FD71 /* Products */;
375 | projectDirPath = "";
376 | projectRoot = "";
377 | targets = (
378 | E7EB66B719554E290069FD71 /* hackfoldr-iOS */,
379 | E7EB66DB19554E290069FD71 /* hackfoldr-iOSTests */,
380 | );
381 | };
382 | /* End PBXProject section */
383 |
384 | /* Begin PBXResourcesBuildPhase section */
385 | E7EB66B619554E290069FD71 /* Resources */ = {
386 | isa = PBXResourcesBuildPhase;
387 | buildActionMask = 2147483647;
388 | files = (
389 | E7EB66D719554E290069FD71 /* Images.xcassets in Resources */,
390 | 88AC6BEA1F6BD13000E082E2 /* LaunchScreen.storyboard in Resources */,
391 | E7EB66C619554E290069FD71 /* InfoPlist.strings in Resources */,
392 | E79B0E7E1E7C030E005BE38C /* g0v.svg in Resources */,
393 | E799373B1FC4C71000DAC153 /* QRCode.storyboard in Resources */,
394 | E755907A1AEAB595007337B1 /* Hackfoldr.strings in Resources */,
395 | );
396 | runOnlyForDeploymentPostprocessing = 0;
397 | };
398 | E7EB66DA19554E290069FD71 /* Resources */ = {
399 | isa = PBXResourcesBuildPhase;
400 | buildActionMask = 2147483647;
401 | files = (
402 | E7EB66E819554E290069FD71 /* InfoPlist.strings in Resources */,
403 | E79424B31ACF015300558AE9 /* sample.csv.json in Resources */,
404 | );
405 | runOnlyForDeploymentPostprocessing = 0;
406 | };
407 | /* End PBXResourcesBuildPhase section */
408 |
409 | /* Begin PBXShellScriptBuildPhase section */
410 | 052512BDB754F2B582BE1463 /* [CP] Check Pods Manifest.lock */ = {
411 | isa = PBXShellScriptBuildPhase;
412 | buildActionMask = 2147483647;
413 | files = (
414 | );
415 | inputPaths = (
416 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
417 | "${PODS_ROOT}/Manifest.lock",
418 | );
419 | name = "[CP] Check Pods Manifest.lock";
420 | outputPaths = (
421 | "$(DERIVED_FILE_DIR)/Pods-hackfoldr-iOS-checkManifestLockResult.txt",
422 | );
423 | runOnlyForDeploymentPostprocessing = 0;
424 | shellPath = /bin/sh;
425 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
426 | showEnvVarsInLog = 0;
427 | };
428 | 69F766257D99B6A5FCB74096 /* [CP] Copy Pods Resources */ = {
429 | isa = PBXShellScriptBuildPhase;
430 | buildActionMask = 2147483647;
431 | files = (
432 | );
433 | inputPaths = (
434 | "${SRCROOT}/Pods/Target Support Files/Pods-hackfoldr-iOS/Pods-hackfoldr-iOS-resources.sh",
435 | "${PODS_ROOT}/FontAwesomeKit/FontAwesomeKit/FontAwesome.otf",
436 | "${PODS_ROOT}/FontAwesomeKit/FontAwesomeKit/foundation-icons.ttf",
437 | "${PODS_ROOT}/FontAwesomeKit/FontAwesomeKit/ionicons.ttf",
438 | "${PODS_ROOT}/FontAwesomeKit/FontAwesomeKit/Material-Design-Iconic-Font.ttf",
439 | "${PODS_ROOT}/FontAwesomeKit/FontAwesomeKit/octicons.ttf",
440 | "${PODS_ROOT}/FontAwesomeKit/FontAwesomeKit/zocial-regular-webfont.ttf",
441 | "${PODS_CONFIGURATION_BUILD_DIR}/TOWebViewController/TOWebViewControllerLocalizable.bundle",
442 | "${PODS_ROOT}/XLForm/XLForm/XLForm.bundle",
443 | );
444 | name = "[CP] Copy Pods Resources";
445 | outputPaths = (
446 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome.otf",
447 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/foundation-icons.ttf",
448 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ionicons.ttf",
449 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Material-Design-Iconic-Font.ttf",
450 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/octicons.ttf",
451 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/zocial-regular-webfont.ttf",
452 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/TOWebViewControllerLocalizable.bundle",
453 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/XLForm.bundle",
454 | );
455 | runOnlyForDeploymentPostprocessing = 0;
456 | shellPath = /bin/sh;
457 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-hackfoldr-iOS/Pods-hackfoldr-iOS-resources.sh\"\n";
458 | showEnvVarsInLog = 0;
459 | };
460 | 77FF7C2AC3EDECEA679C69E0 /* [CP] Check Pods Manifest.lock */ = {
461 | isa = PBXShellScriptBuildPhase;
462 | buildActionMask = 2147483647;
463 | files = (
464 | );
465 | inputPaths = (
466 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
467 | "${PODS_ROOT}/Manifest.lock",
468 | );
469 | name = "[CP] Check Pods Manifest.lock";
470 | outputPaths = (
471 | "$(DERIVED_FILE_DIR)/Pods-hackfoldr-iOSTests-checkManifestLockResult.txt",
472 | );
473 | runOnlyForDeploymentPostprocessing = 0;
474 | shellPath = /bin/sh;
475 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
476 | showEnvVarsInLog = 0;
477 | };
478 | E7118A521AD4FFB7007608C0 /* Update app bundle version */ = {
479 | isa = PBXShellScriptBuildPhase;
480 | buildActionMask = 2147483647;
481 | files = (
482 | );
483 | inputPaths = (
484 | );
485 | name = "Update app bundle version";
486 | outputPaths = (
487 | );
488 | runOnlyForDeploymentPostprocessing = 0;
489 | shellPath = /bin/sh;
490 | shellScript = "exec ./script/xcode-git-version.sh";
491 | };
492 | /* End PBXShellScriptBuildPhase section */
493 |
494 | /* Begin PBXSourcesBuildPhase section */
495 | E7EB66B419554E290069FD71 /* Sources */ = {
496 | isa = PBXSourcesBuildPhase;
497 | buildActionMask = 2147483647;
498 | files = (
499 | E79424B11ACEEF6400558AE9 /* HackfoldrHistory.m in Sources */,
500 | E79B0E7A1E7B1714005BE38C /* HackfoldrClient+Store.m in Sources */,
501 | 886F4A471E0536AA00AF14DE /* NSURL+Hackfoldr.m in Sources */,
502 | E7EB66D519554E290069FD71 /* MainViewController.m in Sources */,
503 | 882E652A19B2D5920054E522 /* ListFieldViewController.m in Sources */,
504 | E77B93D11FAB1F4800BD8130 /* HackfoldrPage+CSSearchableItem.m in Sources */,
505 | E79B0E771E7AE43F005BE38C /* UIColor+Hackfoldr.m in Sources */,
506 | E7B1DD8A195AEE7D00768FFA /* TOWebViewController+HackfoldrField.m in Sources */,
507 | E7EB66CC19554E290069FD71 /* AppDelegate.m in Sources */,
508 | E71D192A1956CE5500B57207 /* HackfoldrClient.m in Sources */,
509 | E79424AE1ACEEDCF00558AE9 /* HackoldrModel.xcdatamodeld in Sources */,
510 | E71D192D1956CFB600B57207 /* HackfoldrField.m in Sources */,
511 | E7F9558719570D4E0098661E /* HackfoldrPage.m in Sources */,
512 | E70F906D1A77F72200BF4B25 /* NSUserDefaults+DefaultHackfoldrPage.m in Sources */,
513 | E7EB66C819554E290069FD71 /* main.m in Sources */,
514 | E79937391FC4C00C00DAC153 /* QRCodeViewController.m in Sources */,
515 | E77B93D41FAB29EC00BD8130 /* SettingViewController.m in Sources */,
516 | );
517 | runOnlyForDeploymentPostprocessing = 0;
518 | };
519 | E7EB66D819554E290069FD71 /* Sources */ = {
520 | isa = PBXSourcesBuildPhase;
521 | buildActionMask = 2147483647;
522 | files = (
523 | E7EB66EA19554E290069FD71 /* hackfoldr_iOSTests.m in Sources */,
524 | );
525 | runOnlyForDeploymentPostprocessing = 0;
526 | };
527 | /* End PBXSourcesBuildPhase section */
528 |
529 | /* Begin PBXTargetDependency section */
530 | E7EB66E219554E290069FD71 /* PBXTargetDependency */ = {
531 | isa = PBXTargetDependency;
532 | target = E7EB66B719554E290069FD71 /* hackfoldr-iOS */;
533 | targetProxy = E7EB66E119554E290069FD71 /* PBXContainerItemProxy */;
534 | };
535 | /* End PBXTargetDependency section */
536 |
537 | /* Begin PBXVariantGroup section */
538 | E75590781AEAB595007337B1 /* Hackfoldr.strings */ = {
539 | isa = PBXVariantGroup;
540 | children = (
541 | E75590791AEAB595007337B1 /* zh-Hant */,
542 | E755907B1AEAB5A0007337B1 /* en */,
543 | );
544 | name = Hackfoldr.strings;
545 | sourceTree = "";
546 | };
547 | E7EB66C419554E290069FD71 /* InfoPlist.strings */ = {
548 | isa = PBXVariantGroup;
549 | children = (
550 | E7EB66C519554E290069FD71 /* en */,
551 | E7A644D31FCEC12D00D28263 /* zh-Hant */,
552 | );
553 | name = InfoPlist.strings;
554 | sourceTree = "";
555 | };
556 | E7EB66E619554E290069FD71 /* InfoPlist.strings */ = {
557 | isa = PBXVariantGroup;
558 | children = (
559 | E7EB66E719554E290069FD71 /* en */,
560 | );
561 | name = InfoPlist.strings;
562 | sourceTree = "";
563 | };
564 | /* End PBXVariantGroup section */
565 |
566 | /* Begin XCBuildConfiguration section */
567 | E7EB66EB19554E290069FD71 /* Debug */ = {
568 | isa = XCBuildConfiguration;
569 | buildSettings = {
570 | ALWAYS_SEARCH_USER_PATHS = NO;
571 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
572 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
573 | CLANG_CXX_LIBRARY = "libc++";
574 | CLANG_ENABLE_MODULES = YES;
575 | CLANG_ENABLE_OBJC_ARC = YES;
576 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
577 | CLANG_WARN_BOOL_CONVERSION = YES;
578 | CLANG_WARN_COMMA = YES;
579 | CLANG_WARN_CONSTANT_CONVERSION = YES;
580 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
581 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
582 | CLANG_WARN_EMPTY_BODY = YES;
583 | CLANG_WARN_ENUM_CONVERSION = YES;
584 | CLANG_WARN_INFINITE_RECURSION = YES;
585 | CLANG_WARN_INT_CONVERSION = YES;
586 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
587 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
588 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
589 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
590 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
591 | CLANG_WARN_STRICT_PROTOTYPES = YES;
592 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
593 | CLANG_WARN_UNREACHABLE_CODE = YES;
594 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
595 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
596 | COPY_PHASE_STRIP = NO;
597 | ENABLE_STRICT_OBJC_MSGSEND = YES;
598 | ENABLE_TESTABILITY = YES;
599 | GCC_C_LANGUAGE_STANDARD = gnu99;
600 | GCC_DYNAMIC_NO_PIC = NO;
601 | GCC_NO_COMMON_BLOCKS = YES;
602 | GCC_OPTIMIZATION_LEVEL = 0;
603 | GCC_PREPROCESSOR_DEFINITIONS = (
604 | "DEBUG=1",
605 | "$(inherited)",
606 | );
607 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
608 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
609 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
610 | GCC_WARN_UNDECLARED_SELECTOR = YES;
611 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
612 | GCC_WARN_UNUSED_FUNCTION = YES;
613 | GCC_WARN_UNUSED_VARIABLE = YES;
614 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
615 | ONLY_ACTIVE_ARCH = YES;
616 | SDKROOT = iphoneos;
617 | TARGETED_DEVICE_FAMILY = "1,2";
618 | };
619 | name = Debug;
620 | };
621 | E7EB66EC19554E290069FD71 /* Release */ = {
622 | isa = XCBuildConfiguration;
623 | buildSettings = {
624 | ALWAYS_SEARCH_USER_PATHS = NO;
625 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
626 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
627 | CLANG_CXX_LIBRARY = "libc++";
628 | CLANG_ENABLE_MODULES = YES;
629 | CLANG_ENABLE_OBJC_ARC = YES;
630 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
631 | CLANG_WARN_BOOL_CONVERSION = YES;
632 | CLANG_WARN_COMMA = YES;
633 | CLANG_WARN_CONSTANT_CONVERSION = YES;
634 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
635 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
636 | CLANG_WARN_EMPTY_BODY = YES;
637 | CLANG_WARN_ENUM_CONVERSION = YES;
638 | CLANG_WARN_INFINITE_RECURSION = YES;
639 | CLANG_WARN_INT_CONVERSION = YES;
640 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
641 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
642 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
643 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
644 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
645 | CLANG_WARN_STRICT_PROTOTYPES = YES;
646 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
647 | CLANG_WARN_UNREACHABLE_CODE = YES;
648 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
649 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
650 | COPY_PHASE_STRIP = YES;
651 | ENABLE_NS_ASSERTIONS = NO;
652 | ENABLE_STRICT_OBJC_MSGSEND = YES;
653 | GCC_C_LANGUAGE_STANDARD = gnu99;
654 | GCC_NO_COMMON_BLOCKS = YES;
655 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
656 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
657 | GCC_WARN_UNDECLARED_SELECTOR = YES;
658 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
659 | GCC_WARN_UNUSED_FUNCTION = YES;
660 | GCC_WARN_UNUSED_VARIABLE = YES;
661 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
662 | SDKROOT = iphoneos;
663 | TARGETED_DEVICE_FAMILY = "1,2";
664 | VALIDATE_PRODUCT = YES;
665 | };
666 | name = Release;
667 | };
668 | E7EB66EE19554E290069FD71 /* Debug */ = {
669 | isa = XCBuildConfiguration;
670 | baseConfigurationReference = D6F8B8E84166B0CC5D012432 /* Pods-hackfoldr-iOS.debug.xcconfig */;
671 | buildSettings = {
672 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
673 | CODE_SIGN_ENTITLEMENTS = "hackfoldr-iOS/hackfoldr-iOS.entitlements";
674 | DEVELOPMENT_TEAM = 67Y6DS58YP;
675 | FRAMEWORK_SEARCH_PATHS = (
676 | "$(inherited)",
677 | "$(PROJECT_DIR)",
678 | );
679 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
680 | GCC_PREFIX_HEADER = "hackfoldr-iOS/hackfoldr-iOS-Prefix.pch";
681 | INFOPLIST_FILE = "hackfoldr-iOS/hackfoldr-iOS-Info.plist";
682 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
683 | PRODUCT_BUNDLE_IDENTIFIER = org.g0v.hackfoldr;
684 | PRODUCT_NAME = "$(TARGET_NAME)";
685 | PROVISIONING_PROFILE = "";
686 | TARGETED_DEVICE_FAMILY = 1;
687 | WRAPPER_EXTENSION = app;
688 | };
689 | name = Debug;
690 | };
691 | E7EB66EF19554E290069FD71 /* Release */ = {
692 | isa = XCBuildConfiguration;
693 | baseConfigurationReference = F15476DD2E850E7A93F2DF39 /* Pods-hackfoldr-iOS.release.xcconfig */;
694 | buildSettings = {
695 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
696 | CODE_SIGN_ENTITLEMENTS = "hackfoldr-iOS/hackfoldr-iOS.entitlements";
697 | DEVELOPMENT_TEAM = 67Y6DS58YP;
698 | FRAMEWORK_SEARCH_PATHS = (
699 | "$(inherited)",
700 | "$(PROJECT_DIR)",
701 | );
702 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
703 | GCC_PREFIX_HEADER = "hackfoldr-iOS/hackfoldr-iOS-Prefix.pch";
704 | INFOPLIST_FILE = "hackfoldr-iOS/hackfoldr-iOS-Info.plist";
705 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
706 | PRODUCT_BUNDLE_IDENTIFIER = org.g0v.hackfoldr;
707 | PRODUCT_NAME = "$(TARGET_NAME)";
708 | PROVISIONING_PROFILE = "";
709 | TARGETED_DEVICE_FAMILY = 1;
710 | WRAPPER_EXTENSION = app;
711 | };
712 | name = Release;
713 | };
714 | E7EB66F119554E290069FD71 /* Debug */ = {
715 | isa = XCBuildConfiguration;
716 | baseConfigurationReference = 70B8275F56498EFBD8F1410F /* Pods-hackfoldr-iOSTests.debug.xcconfig */;
717 | buildSettings = {
718 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/hackfoldr-iOS.app/hackfoldr-iOS";
719 | FRAMEWORK_SEARCH_PATHS = (
720 | "$(SDKROOT)/Developer/Library/Frameworks",
721 | "$(inherited)",
722 | "$(DEVELOPER_FRAMEWORKS_DIR)",
723 | );
724 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
725 | GCC_PREFIX_HEADER = "hackfoldr-iOS/hackfoldr-iOS-Prefix.pch";
726 | GCC_PREPROCESSOR_DEFINITIONS = (
727 | "DEBUG=1",
728 | "$(inherited)",
729 | );
730 | INFOPLIST_FILE = "hackfoldr-iOSTests/hackfoldr-iOSTests-Info.plist";
731 | PRODUCT_BUNDLE_IDENTIFIER = org.g0v.hackfoldr;
732 | PRODUCT_NAME = "$(TARGET_NAME)";
733 | TEST_HOST = "$(BUNDLE_LOADER)";
734 | WRAPPER_EXTENSION = xctest;
735 | };
736 | name = Debug;
737 | };
738 | E7EB66F219554E290069FD71 /* Release */ = {
739 | isa = XCBuildConfiguration;
740 | baseConfigurationReference = 0FFCE6D7B8522BC376B03A51 /* Pods-hackfoldr-iOSTests.release.xcconfig */;
741 | buildSettings = {
742 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/hackfoldr-iOS.app/hackfoldr-iOS";
743 | FRAMEWORK_SEARCH_PATHS = (
744 | "$(SDKROOT)/Developer/Library/Frameworks",
745 | "$(inherited)",
746 | "$(DEVELOPER_FRAMEWORKS_DIR)",
747 | );
748 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
749 | GCC_PREFIX_HEADER = "hackfoldr-iOS/hackfoldr-iOS-Prefix.pch";
750 | INFOPLIST_FILE = "hackfoldr-iOSTests/hackfoldr-iOSTests-Info.plist";
751 | PRODUCT_BUNDLE_IDENTIFIER = org.g0v.hackfoldr;
752 | PRODUCT_NAME = "$(TARGET_NAME)";
753 | TEST_HOST = "$(BUNDLE_LOADER)";
754 | WRAPPER_EXTENSION = xctest;
755 | };
756 | name = Release;
757 | };
758 | /* End XCBuildConfiguration section */
759 |
760 | /* Begin XCConfigurationList section */
761 | E7EB66B319554E290069FD71 /* Build configuration list for PBXProject "hackfoldr-iOS" */ = {
762 | isa = XCConfigurationList;
763 | buildConfigurations = (
764 | E7EB66EB19554E290069FD71 /* Debug */,
765 | E7EB66EC19554E290069FD71 /* Release */,
766 | );
767 | defaultConfigurationIsVisible = 0;
768 | defaultConfigurationName = Release;
769 | };
770 | E7EB66ED19554E290069FD71 /* Build configuration list for PBXNativeTarget "hackfoldr-iOS" */ = {
771 | isa = XCConfigurationList;
772 | buildConfigurations = (
773 | E7EB66EE19554E290069FD71 /* Debug */,
774 | E7EB66EF19554E290069FD71 /* Release */,
775 | );
776 | defaultConfigurationIsVisible = 0;
777 | defaultConfigurationName = Release;
778 | };
779 | E7EB66F019554E290069FD71 /* Build configuration list for PBXNativeTarget "hackfoldr-iOSTests" */ = {
780 | isa = XCConfigurationList;
781 | buildConfigurations = (
782 | E7EB66F119554E290069FD71 /* Debug */,
783 | E7EB66F219554E290069FD71 /* Release */,
784 | );
785 | defaultConfigurationIsVisible = 0;
786 | defaultConfigurationName = Release;
787 | };
788 | /* End XCConfigurationList section */
789 |
790 | /* Begin XCVersionGroup section */
791 | E79424AC1ACEEDCF00558AE9 /* HackoldrModel.xcdatamodeld */ = {
792 | isa = XCVersionGroup;
793 | children = (
794 | E79424AD1ACEEDCF00558AE9 /* HackoldrModel.xcdatamodel */,
795 | );
796 | currentVersion = E79424AD1ACEEDCF00558AE9 /* HackoldrModel.xcdatamodel */;
797 | name = HackoldrModel.xcdatamodeld;
798 | path = "hackfoldr-iOS/HackoldrModel.xcdatamodeld";
799 | sourceTree = "";
800 | versionGroupType = wrapper.xcdatamodel;
801 | };
802 | /* End XCVersionGroup section */
803 | };
804 | rootObject = E7EB66B019554E290069FD71 /* Project object */;
805 | }
806 |
--------------------------------------------------------------------------------