├── Scripts
├── Pods.sh
├── TestResults.sh
├── Sloccount.sh
├── StaticAnalysis.sh
├── Coverage.sh
└── PMD.sh
├── WeatherAppTests
├── Support
│ ├── InfoPlist.strings
│ └── WeatherAppTests-Info.plist
└── Tests
│ ├── CityTests.m
│ └── WeatherAPIManagerTests.m
├── WeatherApp
├── Support
│ ├── Base.lproj
│ │ └── InfoPlist.strings
│ ├── main.m
│ ├── WeatherApp-Prefix.pch
│ ├── Images.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── LaunchImage.launchimage
│ │ │ └── Contents.json
│ ├── Includes.h
│ └── WeatherApp-Info.plist
├── Models
│ ├── City.m
│ └── City.h
├── Controllers
│ ├── CitiesViewController.h
│ ├── SearchViewController.h
│ ├── CityViewController.h
│ ├── CityViewController.m
│ ├── SearchViewController.m
│ └── CitiesViewController.m
├── Application
│ ├── AppDelegate.h
│ └── AppDelegate.m
├── Helpers
│ ├── ValidatorHelper.h
│ ├── ErrorNotificationHelper.h
│ ├── ErrorNotificationHelper.m
│ ├── TranslatorHelper.h
│ ├── ValidatorHelper.m
│ └── TranslatorHelper.m
├── Managers
│ ├── WeatherAPIManager.h
│ └── WeatherAPIManager.m
└── Views
│ └── Storyboard.storyboard
├── .xctool-args
├── Podfile
├── WeatherApp.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ └── WorkspaceSettings.xcsettings
├── WeatherApp.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
├── xcshareddata
│ └── xcschemes
│ │ └── WeatherApp.xcscheme
└── project.pbxproj
├── .gitignore
├── LICENSE
├── Podfile.lock
├── Utils
└── Sloccount-format.xls
└── README.md
/Scripts/Pods.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | pod install
--------------------------------------------------------------------------------
/WeatherAppTests/Support/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/WeatherApp/Support/Base.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/Scripts/TestResults.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | reattach-to-user-namespace xctool -reporter junit:Build/junit.xml clean test
--------------------------------------------------------------------------------
/.xctool-args:
--------------------------------------------------------------------------------
1 | [
2 | "-workspace", "WeatherApp.xcworkspace",
3 | "-scheme", "WeatherApp",
4 | "-sdk", "iphonesimulator"
5 | ]
--------------------------------------------------------------------------------
/Scripts/Sloccount.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | cloc --by-file --xml -out=Build/cloc.xml WeatherApp
3 | xsltproc Utils/Sloccount-format.xls Build/cloc.xml > Build/cloccount.sc
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | platform :ios, '7.0'
2 | pod 'AFNetworking', '~> 2.0'
3 | target :WeatherAppTests, :exclusive => true do
4 | pod 'OCMockito', '~> 1.0.0'
5 | pod 'OCHamcrest', '~> 3.0.0'
6 | end
--------------------------------------------------------------------------------
/WeatherApp.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/WeatherApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Scripts/StaticAnalysis.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | xctool -reporter json-compilation-database:compile_commands.json clean build
3 | oclint-json-compilation-database -e Pods/** -- -max-priority-1 1000 -max-priority-2 1000 -max-priority-3 1000 -report-type pmd -o Build/oclint.xml
--------------------------------------------------------------------------------
/WeatherApp/Models/City.m:
--------------------------------------------------------------------------------
1 | //
2 | // City.m
3 | // WeatherApp
4 | //
5 | // Created by Renzo Crisóstomo on 10/24/13.
6 | // Copyright (c) 2013 Renzo Crisóstomo. All rights reserved.
7 | //
8 |
9 | #import "City.h"
10 |
11 | @implementation City
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/Scripts/Coverage.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | reattach-to-user-namespace xctool clean test OBJROOT=./Build
3 | /opt/scripts/gcovr -r . --object-directory Build/WeatherApp.build/Coverage-iphonesimulator/WeatherApp.build/Objects-normal/i386 --exclude '.*Tests.*' --exclude '.*main.*' --xml > Build/coverage.xml
--------------------------------------------------------------------------------
/Scripts/PMD.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | java -Xmx512m -classpath /Applications/pmd/lib/pmd-4.2.5.jar:/Applications/pmd/lang/ObjCLanguage-0.0.7-SNAPSHOT.jar net.sourceforge.pmd.cpd.CPD --minimum-tokens 10 --language ObjectiveC --encoding UTF-8 --format net.sourceforge.pmd.cpd.XMLRenderer --files WeatherApp --files WeatherAppTests > Build/cpd-output.xml
--------------------------------------------------------------------------------
/WeatherApp.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/WeatherApp/Controllers/CitiesViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // WeatherApp
4 | //
5 | // Created by Renzo Crisóstomo on 10/23/13.
6 | // Copyright (c) 2013 Renzo Crisóstomo. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface CitiesViewController : UITableViewController
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/WeatherApp/Application/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // WeatherApp
4 | //
5 | // Created by Renzo Crisóstomo on 10/23/13.
6 | // Copyright (c) 2013 Renzo Crisóstomo. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/WeatherApp/Controllers/SearchViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // SearchViewController.h
3 | // WeatherApp
4 | //
5 | // Created by Renzo Crisóstomo on 11/3/13.
6 | // Copyright (c) 2013 Renzo Crisóstomo. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface SearchViewController : UITableViewController
12 |
13 | @property (nonatomic, retain) NSMutableArray *dataSource;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/WeatherApp/Support/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // WeatherApp
4 | //
5 | // Created by Renzo Crisóstomo on 10/23/13.
6 | // Copyright (c) 2013 Renzo Crisóstomo. 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 |
--------------------------------------------------------------------------------
/WeatherApp/Support/WeatherApp-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 | #import "Includes.h"
17 | #endif
18 |
--------------------------------------------------------------------------------
/WeatherApp/Support/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "40x40",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "60x60",
16 | "scale" : "2x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/WeatherApp/Helpers/ValidatorHelper.h:
--------------------------------------------------------------------------------
1 | //
2 | // ValidatorHelper.h
3 | // WeatherApp
4 | //
5 | // Created by Renzo Crisóstomo on 10/24/13.
6 | // Copyright (c) 2013 Renzo Crisóstomo. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ValidatorHelper : NSObject
12 |
13 | + (BOOL)isResponseObjectValid:(id)responseObject;
14 | + (BOOL)isCityObjectValid:(id)cityObject;
15 | + (BOOL)isCoordinatesObjectValid:(id)coordinateObject;
16 | + (BOOL)isMainObjectValid:(id)mainObject;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | .DS_Store
3 | */build/*
4 | *.pbxuser
5 | !default.pbxuser
6 | *.mode1v3
7 | !default.mode1v3
8 | *.mode2v3
9 | !default.mode2v3
10 | *.perspectivev3
11 | !default.perspectivev3
12 | xcuserdata
13 | profile
14 | *.moved-aside
15 | DerivedData
16 | .idea/
17 | *.hmap
18 | *.xccheckout
19 |
20 | # CocoaPods
21 | Pods
22 |
23 | # Build output
24 | Build/
25 |
26 | # OSX
27 | .DS_Store
28 | .AppleDouble
29 | .LSOverride
30 | Icon
31 |
32 | # Thumbnails
33 | ._*
34 |
35 | # Files that might appear on external disk
36 | .Spotlight-V100
37 | .Trashes
--------------------------------------------------------------------------------
/WeatherApp/Controllers/CityViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // CityViewController.h
3 | // WeatherApp
4 | //
5 | // Created by Renzo Crisóstomo on 11/3/13.
6 | // Copyright (c) 2013 Renzo Crisóstomo. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class City;
12 |
13 | @interface CityViewController : UITableViewController
14 |
15 | @property (nonatomic, strong) City *city;
16 | @property (nonatomic, weak) IBOutlet UILabel *lblTemperature;
17 | @property (nonatomic, weak) IBOutlet UILabel *lblPressure;
18 | @property (nonatomic, weak) IBOutlet UILabel *lblHumidity;
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/WeatherApp/Models/City.h:
--------------------------------------------------------------------------------
1 | //
2 | // City.h
3 | // WeatherApp
4 | //
5 | // Created by Renzo Crisóstomo on 10/24/13.
6 | // Copyright (c) 2013 Renzo Crisóstomo. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface City : NSObject
12 |
13 | @property (nonatomic, copy) NSString *name;
14 | @property (nonatomic, copy) NSNumber *latitude;
15 | @property (nonatomic, copy) NSNumber *longitude;
16 | @property (nonatomic, copy) NSNumber *temperature;
17 | @property (nonatomic, copy) NSNumber *pressure;
18 | @property (nonatomic, copy) NSNumber *humidity;
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/WeatherApp/Support/Images.xcassets/LaunchImage.launchimage/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "orientation" : "portrait",
5 | "idiom" : "iphone",
6 | "extent" : "full-screen",
7 | "minimum-system-version" : "7.0",
8 | "scale" : "2x"
9 | },
10 | {
11 | "orientation" : "portrait",
12 | "idiom" : "iphone",
13 | "subtype" : "retina4",
14 | "extent" : "full-screen",
15 | "minimum-system-version" : "7.0",
16 | "scale" : "2x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/WeatherApp/Helpers/ErrorNotificationHelper.h:
--------------------------------------------------------------------------------
1 | //
2 | // ErrorNotificationHelper.h
3 | // WeatherApp
4 | //
5 | // Created by Renzo Crisóstomo on 10/24/13.
6 | // Copyright (c) 2013 Renzo Crisóstomo. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @protocol ErrorNotificationHelperDelegate
12 |
13 | @required
14 |
15 | - (void)didNotifyError:(NSError *)error
16 | withCallbackBlock:(CallbackBlock)callbackBlock;
17 |
18 | @end
19 |
20 | @interface ErrorNotificationHelper : NSObject
21 |
22 | @property (nonatomic, weak) id delegate;
23 |
24 | - (void)notifyError:(NSError *)error
25 | withCallbackBlock:(CallbackBlock)callbackBlock;
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/WeatherAppTests/Support/WeatherAppTests-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | com.ruenzuo.${PRODUCT_NAME:rfc1034identifier}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundlePackageType
14 | BNDL
15 | CFBundleShortVersionString
16 | 1.0
17 | CFBundleSignature
18 | ????
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/WeatherApp/Helpers/ErrorNotificationHelper.m:
--------------------------------------------------------------------------------
1 | //
2 | // ErrorNotificationHelper.m
3 | // WeatherApp
4 | //
5 | // Created by Renzo Crisóstomo on 10/24/13.
6 | // Copyright (c) 2013 Renzo Crisóstomo. All rights reserved.
7 | //
8 |
9 | #import "ErrorNotificationHelper.h"
10 |
11 | @implementation ErrorNotificationHelper
12 | {
13 | }
14 |
15 | #pragma mark - Public Methods
16 |
17 | - (void)notifyError:(NSError *)error
18 | withCallbackBlock:(CallbackBlock)callbackBlock;
19 | {
20 | NSLog(@"Error getting cities: %@", [error localizedDescription]);
21 | if ([_delegate respondsToSelector:@selector(didNotifyError:withCallbackBlock:)]) {
22 | [_delegate performSelector:@selector(didNotifyError:withCallbackBlock:)
23 | withObject:error
24 | withObject:callbackBlock];
25 | }
26 | }
27 |
28 | @end
29 |
--------------------------------------------------------------------------------
/WeatherApp/Helpers/TranslatorHelper.h:
--------------------------------------------------------------------------------
1 | //
2 | // ParseHelper.h
3 | // WeatherApp
4 | //
5 | // Created by Renzo Crisóstomo on 10/24/13.
6 | // Copyright (c) 2013 Renzo Crisóstomo. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class ValidatorHelper;
12 |
13 | @protocol TranslatorHelperDelegate
14 |
15 | @required
16 |
17 | - (void)didSucceedParseCities:(NSArray *)cities
18 | withCallbackBlock:(CallbackBlock)callbackBlock;
19 | - (void)didFailParseCitiesWithError:(NSError *)error
20 | andCallbackBlock:(CallbackBlock)callbackBlock;
21 |
22 | @end
23 |
24 | @interface TranslatorHelper : NSObject
25 |
26 | @property (nonatomic, weak) id delegate;
27 |
28 | - (void)parseCitiesWithResponseObject:(id)responseObject
29 | andCallbackBlock:(CallbackBlock)callbackBlock;
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/WeatherApp/Support/Includes.h:
--------------------------------------------------------------------------------
1 | //
2 | // Includes.h
3 | // WeatherApp
4 | //
5 | // Created by Renzo Crisóstomo on 10/24/13.
6 | // Copyright (c) 2013 Renzo Crisóstomo. All rights reserved.
7 | //
8 |
9 | #ifndef WeatherApp_Includes_h
10 | #define WeatherApp_Includes_h
11 |
12 | typedef void (^CallbackBlock) (NSArray *cities, NSError *error);
13 |
14 | #define JSON_VALID_STRING @"{\"message\":0.0097,\"cod\":\"200\",\"calctime\":\"\",\"cnt\":1,\"list\":[{\"id\":5391959,\"name\":\"San Francisco\",\"coord\":{\"lon\":-122.419418,\"lat\":37.774929},\"distance\":1.869,\"main\":{\"temp\":291.11,\"pressure\":1019,\"humidity\":51,\"temp_min\":286.48,\"temp_max\":295.37},\"dt\":1363564220,\"wind\":{\"speed\":8.2,\"deg\":280},\"clouds\":{\"all\":75},\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04d\"}]}]}"
15 | #define JSON_INVALID_STRING @"{ergnlsgzkuyghlzfnz§fgibuzf}"
16 |
17 | #endif
18 |
--------------------------------------------------------------------------------
/WeatherApp/Managers/WeatherAPIManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // WeatherAPIManager.h
3 | // WeatherApp
4 | //
5 | // Created by Renzo Crisóstomo on 10/24/13.
6 | // Copyright (c) 2013 Renzo Crisóstomo. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import "TranslatorHelper.h"
12 | #import "ErrorNotificationHelper.h"
13 |
14 | @interface WeatherAPIManager : NSObject
15 |
16 | @property (nonatomic, strong) AFHTTPRequestOperationManager *requestOperationManager;
17 | @property (nonatomic, strong) TranslatorHelper *translatorHelper;
18 | @property (nonatomic, strong) ErrorNotificationHelper *errorNotificationHelper;
19 |
20 | + (WeatherAPIManager *)sharedManager;
21 |
22 | - (void)citiesWithUserLatitude:(NSNumber *)userLatitude
23 | userLongitude:(NSNumber *)userLongitude
24 | andCallbackBlock:(CallbackBlock)callbackBlock;
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Renzo Crisóstomo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - AFNetworking (2.0.1):
3 | - AFNetworking/NSURLConnection
4 | - AFNetworking/NSURLSession
5 | - AFNetworking/Reachability
6 | - AFNetworking/Security
7 | - AFNetworking/Serialization
8 | - AFNetworking/UIKit
9 | - AFNetworking/NSURLConnection (2.0.1):
10 | - AFNetworking/Reachability
11 | - AFNetworking/Security
12 | - AFNetworking/Serialization
13 | - AFNetworking/NSURLSession (2.0.1):
14 | - AFNetworking/Reachability
15 | - AFNetworking/Security
16 | - AFNetworking/Serialization
17 | - AFNetworking/Reachability (2.0.1)
18 | - AFNetworking/Security (2.0.1)
19 | - AFNetworking/Serialization (2.0.1)
20 | - AFNetworking/UIKit (2.0.1):
21 | - AFNetworking/NSURLConnection
22 | - OCHamcrest (3.0.0)
23 | - OCMockito (1.0.0):
24 | - OCHamcrest (~> 3.0)
25 |
26 | DEPENDENCIES:
27 | - AFNetworking (~> 2.0)
28 | - OCHamcrest (~> 3.0.0)
29 | - OCMockito (~> 1.0.0)
30 |
31 | SPEC CHECKSUMS:
32 | AFNetworking: a6f11ac4ac087303e6ff87adc1ba57b0dac20ef8
33 | OCHamcrest: 739added27df87b26adb60ab5aa14b7e3cf58afe
34 | OCMockito: 8b6a244d7d81ba3cd2095bc357c20ae7b22c8977
35 |
36 | COCOAPODS: 0.28.0
37 |
--------------------------------------------------------------------------------
/WeatherApp/Controllers/CityViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // CityViewController.m
3 | // WeatherApp
4 | //
5 | // Created by Renzo Crisóstomo on 11/3/13.
6 | // Copyright (c) 2013 Renzo Crisóstomo. All rights reserved.
7 | //
8 |
9 | #import "CityViewController.h"
10 | #import "City.h"
11 |
12 | @interface CityViewController ()
13 |
14 | @end
15 |
16 | @implementation CityViewController
17 | {
18 | }
19 |
20 | #pragma mark - View Controller Lifecycle
21 |
22 | - (id)initWithCoder:(NSCoder *)aDecoder
23 | {
24 | self = [super initWithCoder:aDecoder];
25 | if (self) {
26 |
27 | }
28 | return self;
29 | }
30 |
31 | - (void)viewDidLoad
32 | {
33 | [super viewDidLoad];
34 | [self setupView];
35 | }
36 |
37 | - (void)didReceiveMemoryWarning
38 | {
39 | [super didReceiveMemoryWarning];
40 | }
41 |
42 | #pragma mark - Private Methods
43 |
44 | - (void)setupView
45 | {
46 | _lblHumidity.text = [NSString stringWithFormat:@"%.1f %%", [_city.humidity floatValue]];
47 | _lblPressure.text = [NSString stringWithFormat:@"%.1f hpa", [_city.pressure floatValue]];
48 | _lblTemperature.text = [NSString stringWithFormat:@"%.1f °K", [_city.temperature floatValue]];
49 | self.title = _city.name;
50 | }
51 |
52 | @end
53 |
--------------------------------------------------------------------------------
/WeatherApp/Support/WeatherApp-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UIViewControllerBasedStatusBarAppearance
6 |
7 | CFBundleDevelopmentRegion
8 | en
9 | CFBundleDisplayName
10 | ${PRODUCT_NAME}
11 | CFBundleExecutable
12 | ${EXECUTABLE_NAME}
13 | CFBundleIdentifier
14 | com.ruenzuo.${PRODUCT_NAME:rfc1034identifier}
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleName
18 | ${PRODUCT_NAME}
19 | CFBundlePackageType
20 | APPL
21 | CFBundleShortVersionString
22 | 1.0
23 | CFBundleSignature
24 | ????
25 | CFBundleVersion
26 | 1.0
27 | LSRequiresIPhoneOS
28 |
29 | UIMainStoryboardFile
30 | Storyboard
31 | UIRequiredDeviceCapabilities
32 |
33 | armv7
34 |
35 | UISupportedInterfaceOrientations
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationLandscapeLeft
39 | UIInterfaceOrientationLandscapeRight
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/WeatherApp/Helpers/ValidatorHelper.m:
--------------------------------------------------------------------------------
1 | //
2 | // ValidatorHelper.m
3 | // WeatherApp
4 | //
5 | // Created by Renzo Crisóstomo on 10/24/13.
6 | // Copyright (c) 2013 Renzo Crisóstomo. All rights reserved.
7 | //
8 |
9 | #import "ValidatorHelper.h"
10 |
11 | @implementation ValidatorHelper
12 | {
13 | }
14 |
15 | #pragma mark - Class Methods
16 |
17 | + (BOOL)isResponseObjectValid:(id)responseObject
18 | {
19 | return (responseObject != nil &&
20 | [responseObject isKindOfClass:[NSDictionary class]] &&
21 | [responseObject objectForKey:@"list"] != nil);
22 | }
23 |
24 | + (BOOL)isCityObjectValid:(id)cityObject
25 | {
26 | return (cityObject != nil &&
27 | [cityObject isKindOfClass:[NSDictionary class]] &&
28 | [cityObject objectForKey:@"name"] != nil &&
29 | [cityObject objectForKey:@"coord"] != nil &&
30 | [cityObject objectForKey:@"main"] != nil);
31 | }
32 |
33 | + (BOOL)isCoordinatesObjectValid:(id)coordinateObject
34 | {
35 | return (coordinateObject != nil &&
36 | [coordinateObject isKindOfClass:[NSDictionary class]] &&
37 | [coordinateObject objectForKey:@"lat"] != nil &&
38 | [coordinateObject objectForKey:@"lon"] != nil);
39 | }
40 |
41 | + (BOOL)isMainObjectValid:(id)mainObject
42 | {
43 | return (mainObject != nil &&
44 | [mainObject isKindOfClass:[NSDictionary class]] &&
45 | [mainObject objectForKey:@"humidity"] != nil &&
46 | [mainObject objectForKey:@"pressure"] != nil &&
47 | [mainObject objectForKey:@"temp"] != nil);
48 | }
49 |
50 | @end
51 |
--------------------------------------------------------------------------------
/WeatherApp/Application/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // WeatherApp
4 | //
5 | // Created by Renzo Crisóstomo on 10/23/13.
6 | // Copyright (c) 2013 Renzo Crisóstomo. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 | #import
11 |
12 | @interface AppDelegate ()
13 |
14 | - (void)setupAppearance;
15 |
16 | @end
17 |
18 | @implementation AppDelegate
19 | {
20 | }
21 |
22 | #pragma mark - Application Life Cycle
23 |
24 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
25 | {
26 | [self setupAppearance];
27 | return YES;
28 | }
29 |
30 | - (void)applicationWillResignActive:(UIApplication *)application
31 | {
32 |
33 | }
34 |
35 | - (void)applicationDidEnterBackground:(UIApplication *)application
36 | {
37 |
38 | }
39 |
40 | - (void)applicationWillEnterForeground:(UIApplication *)application
41 | {
42 |
43 | }
44 |
45 | - (void)applicationDidBecomeActive:(UIApplication *)application
46 | {
47 |
48 | }
49 |
50 | - (void)applicationWillTerminate:(UIApplication *)application
51 | {
52 |
53 | }
54 |
55 | #pragma mark - Private Methods
56 |
57 | - (void)setupAppearance
58 | {
59 | [[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES];
60 | [[self window] setTintColor:[UIColor whiteColor]];
61 | [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
62 | [[UINavigationBar appearance] setTitleTextAttributes:[NSDictionary dictionaryWithObject:[UIColor whiteColor]
63 | forKey:NSForegroundColorAttributeName]];
64 | }
65 |
66 | @end
67 |
--------------------------------------------------------------------------------
/Utils/Sloccount-format.xls:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | For more details see:
23 |
24 |
25 |
26 | This report has been generated by cloc .
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/WeatherAppTests/Tests/CityTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // CityTests.m
3 | // WeatherApp
4 | //
5 | // Created by Renzo Crisóstomo on 10/24/13.
6 | // Copyright (c) 2013 Renzo Crisóstomo. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #define HC_SHORTHAND
12 | #import
13 | #define MOCKITO_SHORTHAND
14 | #import
15 | #import "City.h"
16 |
17 | @interface CityTests : XCTestCase
18 | {
19 | City *_city;
20 | }
21 |
22 | @end
23 |
24 | extern void __gcov_flush();
25 |
26 | @implementation CityTests
27 |
28 | - (void)setUp
29 | {
30 | [super setUp];
31 | _city = [[City alloc] init];
32 | }
33 |
34 | - (void)tearDown
35 | {
36 | __gcov_flush();
37 | [super tearDown];
38 | }
39 |
40 | #pragma mark - Property Tests
41 |
42 | - (void)testNameProperty
43 | {
44 | NSMutableString *cityName = [NSMutableString stringWithString:@"Lima"];
45 | [_city setName:cityName];
46 | [cityName setString:@"Chiclayo"];
47 | assertThat(_city.name, instanceOf([NSString class]));
48 | assertThat(_city.name, isNot(sameInstance(cityName)));
49 | assertThat(_city.name, is(@"Lima"));
50 | }
51 |
52 | - (void)testLatitudeProperty
53 | {
54 | NSNumber *cityLatitude = [NSNumber numberWithInt:100];
55 | [_city setLatitude:cityLatitude];
56 | assertThat(_city.latitude, instanceOf([NSNumber class]));
57 | }
58 |
59 | - (void)testLongitudeProperty
60 | {
61 | NSNumber *cityLongitude = [NSNumber numberWithInt:100];
62 | [_city setLongitude:cityLongitude];
63 | assertThat(_city.longitude, instanceOf([NSNumber class]));
64 | }
65 |
66 | - (void)testTemperatureProperty
67 | {
68 | NSNumber *cityTemperature = [NSNumber numberWithInt:100];
69 | [_city setTemperature:cityTemperature];
70 | assertThat(_city.temperature, instanceOf([NSNumber class]));
71 | }
72 |
73 | - (void)testPressureProperty
74 | {
75 | NSNumber *cityPressure = [NSNumber numberWithInt:100];
76 | [_city setPressure:cityPressure];
77 | assertThat(_city.pressure, instanceOf([NSNumber class]));
78 | }
79 |
80 | - (void)testHumidityProperty
81 | {
82 | NSNumber *cityHumidity = [NSNumber numberWithInt:100];
83 | [_city setHumidity:cityHumidity];
84 | assertThat(_city.humidity, instanceOf([NSNumber class]));
85 | }
86 |
87 | @end
88 |
--------------------------------------------------------------------------------
/WeatherApp/Helpers/TranslatorHelper.m:
--------------------------------------------------------------------------------
1 | //
2 | // ParseHelper.m
3 | // WeatherApp
4 | //
5 | // Created by Renzo Crisóstomo on 10/24/13.
6 | // Copyright (c) 2013 Renzo Crisóstomo. All rights reserved.
7 | //
8 |
9 | #import "TranslatorHelper.h"
10 | #import "City.h"
11 | #import "ValidatorHelper.h"
12 |
13 | @interface TranslatorHelper ()
14 |
15 | - (void)notifyErrorWithCallbackBlock:(CallbackBlock)callbackBlock;
16 |
17 | @end
18 |
19 | @implementation TranslatorHelper
20 | {
21 | }
22 |
23 | #pragma mark - Private Methods
24 |
25 | - (void)notifyErrorWithCallbackBlock:(CallbackBlock)callbackBlock
26 | {
27 | NSError *error = [NSError errorWithDomain:@"com.ruenzuo.WeatherApp"
28 | code:20091990
29 | userInfo:[NSDictionary dictionaryWithObject:@"Wrong response object structure"
30 | forKey:@"Description"]];
31 | if ([_delegate respondsToSelector:@selector(didFailParseCitiesWithError:andCallbackBlock:)]) {
32 | [_delegate performSelector:@selector(didFailParseCitiesWithError:andCallbackBlock:)
33 | withObject:error
34 | withObject:callbackBlock];
35 | }
36 | }
37 |
38 | #pragma mark - Public Methods
39 |
40 | - (void)parseCitiesWithResponseObject:(id)responseObject
41 | andCallbackBlock:(CallbackBlock)callbackBlock
42 | {
43 | NSMutableArray *cities = [[NSMutableArray alloc] init];
44 | if ([ValidatorHelper isResponseObjectValid:responseObject]) {
45 | NSArray *jsonCities = [responseObject objectForKey:@"list"];
46 | for (id cityObject in jsonCities) {
47 | if ([ValidatorHelper isCityObjectValid:cityObject]) {
48 | City *city = [[City alloc] init];
49 | city.name = [cityObject objectForKey:@"name"];
50 | id coordinatesObject = [cityObject objectForKey:@"coord"];
51 | if ([ValidatorHelper isCoordinatesObjectValid:coordinatesObject]) {
52 | city.latitude = [coordinatesObject objectForKey:@"lat"];
53 | city.longitude = [coordinatesObject objectForKey:@"lon"];
54 | }
55 | else {
56 | [self notifyErrorWithCallbackBlock:callbackBlock];
57 | break;
58 | }
59 | id mainObject = [cityObject objectForKey:@"main"];
60 | if ([ValidatorHelper isMainObjectValid:mainObject]) {
61 | city.temperature = [mainObject objectForKey:@"temp"];
62 | city.pressure = [mainObject objectForKey:@"pressure"];
63 | city.humidity = [mainObject objectForKey:@"humidity"];
64 | }
65 | else {
66 | [self notifyErrorWithCallbackBlock:callbackBlock];
67 | break;
68 | }
69 | [cities addObject:city];
70 | }
71 | else {
72 | [self notifyErrorWithCallbackBlock:callbackBlock];
73 | break;
74 | }
75 | }
76 | }
77 | else {
78 | [self notifyErrorWithCallbackBlock:callbackBlock];
79 | }
80 | if ([_delegate respondsToSelector:@selector(didSucceedParseCities:withCallbackBlock:)]) {
81 | [_delegate performSelector:@selector(didSucceedParseCities:withCallbackBlock:)
82 | withObject:cities
83 | withObject:callbackBlock];
84 | }
85 | }
86 |
87 | @end
88 |
--------------------------------------------------------------------------------
/WeatherApp/Managers/WeatherAPIManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // WeatherAPIManager.m
3 | // WeatherApp
4 | //
5 | // Created by Renzo Crisóstomo on 10/24/13.
6 | // Copyright (c) 2013 Renzo Crisóstomo. All rights reserved.
7 | //
8 |
9 | #import "WeatherAPIManager.h"
10 |
11 | static dispatch_once_t oncePredicate;
12 |
13 | @implementation WeatherAPIManager
14 | {
15 | }
16 |
17 | #pragma mark - Lazy Loading Pattern
18 |
19 | - (AFHTTPRequestOperationManager *)requestOperationManager
20 | {
21 | if (_requestOperationManager == nil) {
22 | _requestOperationManager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:
23 | [NSURL URLWithString:@"http://api.openweathermap.org/data/2.5/"]];
24 | _requestOperationManager.responseSerializer = [AFJSONResponseSerializer serializer];
25 | _requestOperationManager.requestSerializer = [AFJSONRequestSerializer serializer];
26 | }
27 | return _requestOperationManager;
28 | }
29 |
30 | - (TranslatorHelper *)translatorHelper
31 | {
32 | if (_translatorHelper == nil) {
33 | _translatorHelper = [[TranslatorHelper alloc] init];
34 | _translatorHelper.delegate = self;
35 | }
36 | return _translatorHelper;
37 | }
38 |
39 | - (ErrorNotificationHelper *)errorNotificationHelper
40 | {
41 | if (_errorNotificationHelper == nil) {
42 | _errorNotificationHelper = [[ErrorNotificationHelper alloc] init];
43 | _errorNotificationHelper.delegate = self;
44 | }
45 | return _errorNotificationHelper;
46 | }
47 |
48 | #pragma mark - Class Methods
49 |
50 | + (WeatherAPIManager *)sharedManager
51 | {
52 | static WeatherAPIManager *_sharedManager;
53 | dispatch_once(&oncePredicate, ^{
54 | _sharedManager = [[self alloc] init];
55 | });
56 | return _sharedManager;
57 | }
58 |
59 | #pragma mark - Public Methods
60 |
61 | - (void)citiesWithUserLatitude:(NSNumber *)userLatitude
62 | userLongitude:(NSNumber *)userLongitude
63 | andCallbackBlock:(CallbackBlock)callbackBlock
64 | {
65 | __weak typeof(self) blocksafeSelf = self;
66 | [[self requestOperationManager] GET:@"find"
67 | parameters:[NSDictionary dictionaryWithObjectsAndKeys:userLatitude,
68 | @"lat", userLongitude, @"lon", [NSNumber numberWithInt:100], @"cnt", @"json", @"mode", nil]
69 | success:^(AFHTTPRequestOperation *operation, id responseObject) {
70 | [[blocksafeSelf translatorHelper] parseCitiesWithResponseObject:responseObject
71 | andCallbackBlock:callbackBlock];
72 | }
73 | failure:^(AFHTTPRequestOperation *operation, NSError *error) {
74 | [[blocksafeSelf errorNotificationHelper] notifyError:error
75 | withCallbackBlock:callbackBlock];
76 | }];
77 | }
78 |
79 | #pragma mark - ParseHelperDelegate
80 |
81 | - (void)didSucceedParseCities:(NSArray *)cities
82 | withCallbackBlock:(CallbackBlock)callbackBlock
83 | {
84 | callbackBlock(cities, nil);
85 | }
86 |
87 | - (void)didFailParseCitiesWithError:(NSError *)error
88 | andCallbackBlock:(CallbackBlock)callbackBlock
89 | {
90 | [[self errorNotificationHelper] notifyError:error
91 | withCallbackBlock:callbackBlock];
92 | }
93 |
94 | #pragma mark - ErrorNotificationHelperDelegate
95 |
96 | - (void)didNotifyError:(NSError *)error
97 | withCallbackBlock:(CallbackBlock)callbackBlock
98 | {
99 | callbackBlock(nil, error);
100 | }
101 |
102 | @end
103 |
--------------------------------------------------------------------------------
/WeatherApp.xcodeproj/xcshareddata/xcschemes/WeatherApp.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
61 |
62 |
68 |
69 |
70 |
71 |
72 |
73 |
79 |
80 |
86 |
87 |
88 |
89 |
91 |
92 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/WeatherApp/Controllers/SearchViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // SearchViewController.m
3 | // WeatherApp
4 | //
5 | // Created by Renzo Crisóstomo on 11/3/13.
6 | // Copyright (c) 2013 Renzo Crisóstomo. All rights reserved.
7 | //
8 |
9 | #import "SearchViewController.h"
10 | #import "City.h"
11 | #import "CityViewController.h"
12 |
13 | @interface SearchViewController ()
14 | {
15 | NSMutableArray *_filteredDataSource;
16 | }
17 |
18 | - (void)setupSearchDisplayController;
19 | - (void)filterContentForSearchText:(NSString*)searchText;
20 |
21 | @end
22 |
23 | @implementation SearchViewController
24 | {
25 | }
26 |
27 | #pragma mark - View Controller Lifecycle
28 |
29 | - (id)initWithCoder:(NSCoder *)aDecoder
30 | {
31 | self = [super initWithCoder:aDecoder];
32 | if (self) {
33 |
34 | }
35 | return self;
36 | }
37 |
38 | - (void)viewDidLoad
39 | {
40 | [super viewDidLoad];
41 | [self setupSearchDisplayController];
42 | }
43 |
44 | - (void)viewDidAppear:(BOOL)animated
45 | {
46 | [self.searchDisplayController.searchBar becomeFirstResponder];
47 | }
48 |
49 | - (void)didReceiveMemoryWarning
50 | {
51 | [super didReceiveMemoryWarning];
52 | }
53 |
54 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
55 | {
56 | if ([[segue identifier] isEqualToString:@"CitySegue"]) {
57 | CityViewController *viewController = (CityViewController *)[segue destinationViewController];
58 | NSIndexPath *selectedIndexPath = [self.searchDisplayController.searchResultsTableView
59 | indexPathForSelectedRow];
60 | City *city = [_filteredDataSource objectAtIndex:selectedIndexPath.row];
61 | viewController.city = city;
62 | }
63 | }
64 |
65 | #pragma mark - Lazy Loading Pattern
66 |
67 | - (void)setDataSource:(NSMutableArray *)dataSource
68 | {
69 | _dataSource = dataSource;
70 | _filteredDataSource = [NSMutableArray arrayWithCapacity:[_dataSource count]];
71 | }
72 |
73 | #pragma mark - Private Methods
74 |
75 | - (void)setupSearchDisplayController
76 | {
77 | self.searchDisplayController.displaysSearchBarInNavigationBar = YES;
78 | UITextField *searchField = [self.searchDisplayController.searchBar valueForKey:@"_searchField"];
79 | searchField.tintColor = [UIColor colorWithRed:46.0f/255.0f
80 | green:204.0f/255.0f
81 | blue:113.0f/255.0f
82 | alpha:1.0f];
83 | }
84 |
85 | -(void)filterContentForSearchText:(NSString*)searchText
86 | {
87 | [_filteredDataSource removeAllObjects];
88 | NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.name contains[c] %@", searchText];
89 | [_filteredDataSource addObjectsFromArray:[_dataSource filteredArrayUsingPredicate:predicate]];
90 | }
91 |
92 | #pragma mark - UITableViewDataSource
93 |
94 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
95 | {
96 | if (tableView == self.searchDisplayController.searchResultsTableView) {
97 | return [_filteredDataSource count];
98 | }
99 | else {
100 | return 0;
101 | }
102 | }
103 |
104 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
105 | {
106 | static NSString *CellIdentifier = @"CityCell";
107 | UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
108 | City *city = nil;
109 | if (tableView == self.searchDisplayController.searchResultsTableView) {
110 | city = [_filteredDataSource objectAtIndex:indexPath.row];
111 | cell.textLabel.text = city.name;
112 | cell.detailTextLabel.text = [NSString stringWithFormat:@"Lat: %.2f, Lon: %.2f",
113 | [city.latitude floatValue],[city.longitude floatValue]];
114 | }
115 | return cell;
116 | }
117 |
118 | #pragma mark - UISearchDisplayDelegate
119 |
120 | - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
121 | {
122 | [self filterContentForSearchText:searchString];
123 | return YES;
124 | }
125 |
126 | - (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller
127 | {
128 | [self.navigationController popViewControllerAnimated:YES];
129 | }
130 |
131 | @end
132 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Continuous Integration on iOS with Jenkins
2 | =========================================
3 |
4 | __Description:__
5 |
6 | This is a continuous integration example with an iOS application using xctool as the build system, CocoaPods as the dependency manager, AFNetworking for asynchronous networking inside the iOS app, OCMockito for mock and stub unit testing, OCHamcrest for unit testing matchers, gcovr for test coverage reporting, oclint for static analysis reporting, cloc for software lines of code count reporting, PMD for duplicate code reporting and Jenkins as the continuous integration server. You can find the detail setup and explanations for all these in my [blog](http://ruenzuo.github.io/static-analysis-on-ios-part-i/index.html).
7 |
8 | __Building:__
9 |
10 | In order to build the application, clone this repo:
11 |
12 | ```sh
13 | $ git clone git@github.com:Ruenzuo/ios-ci-jenkins-example.git
14 | ```
15 |
16 | Then set up the dependencies and open the workspace and you're ready to go:
17 |
18 | ```sh
19 | $ cd ios-ci-jenkins-example && pod install && open WeatherApp.xcworkspace
20 | ```
21 |
22 | 
23 | 
24 | 
25 |
26 | __Jenkins Dashboard:__
27 |
28 | The main objective here is to have a quality metrics dashboard on Jenkins:
29 |
30 | 
31 |
32 | 
33 |
34 | 
35 |
36 | 
37 |
38 | 
39 |
40 | You only need to add an Execute shell step on you Jenkins job like this:
41 |
42 | ```sh
43 | ./Scripts/Pods.sh
44 | ./Scripts/Coverage.sh
45 | ./Scripts/StaticAnalysis.sh
46 | ./Scripts/PMD.sh
47 | ./Scripts/Sloccount.sh
48 | ./Scripts/TestResults.sh
49 | ```
50 |
51 | For more information on how to get started with each tool/framework, go to:
52 | [xctool](https://github.com/facebook/xctool)
53 | [CocoaPods](https://github.com/cocoapods/cocoapods)
54 | [AFNetworking](https://github.com/AFNetworking/AFNetworking)
55 | [OCMockito](https://github.com/jonreid/OCMockito)
56 | [OCHamcrest](https://github.com/hamcrest/OCHamcrest)
57 | [gcovr](https://github.com/gcovr/gcovr)
58 | [oclint](https://github.com/oclint/oclint)
59 | [cloc](http://cloc.sourceforge.net)
60 | [PMD](http://pmd.sourceforge.net)
61 | [Jenkins](http://jenkins-ci.org/)
62 |
63 | The following Jenkins plugins have been used for the dashboard:
64 | Test results: Regular Jenkins JUnit test result report plugin
65 | [Code coverage](https://wiki.jenkins-ci.org/display/JENKINS/Cobertura+Plugin)
66 | [Static analysis](https://wiki.jenkins-ci.org/display/JENKINS/PMD+Plugin)
67 | [Sloc count](https://wiki.jenkins-ci.org/display/JENKINS/SLOCCount+Plugin)
68 | [Duplicate code](https://wiki.jenkins-ci.org/display/JENKINS/DRY+Plugin)
69 |
70 | If you examine closely the scripts, this helpers tools have been used:
71 | [reattach-to-user-namespace](https://github.com/ChrisJohnsen/tmux-MacOSX-pasteboard)
72 | [Objective-C-CPD-Language](https://github.com/jkennedy1980/Objective-C-CPD-Language)
73 | [xsltproc](https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/xsltproc.1.html)
74 |
75 | __To-Do__
76 |
77 | * `[✓]` Make it public.
78 | * `[✓]` Write the blog post. It's [here](http://ruenzuo.github.io/static-analysis-on-ios-part-i/index.html).
79 | * `[ ]` Continuous delivery setup: app signing and Testflight integration.
80 | * `[ ]` Currently, I'm executing the unit tests twice, because I'm using two different reporters each time. I think this could be improved.
81 |
82 | License
83 | =======
84 |
85 | The MIT License (MIT)
86 |
87 | Copyright (c) 2014 Renzo Crisóstomo
88 |
89 | Permission is hereby granted, free of charge, to any person obtaining a copy of
90 | this software and associated documentation files (the "Software"), to deal in
91 | the Software without restriction, including without limitation the rights to
92 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
93 | the Software, and to permit persons to whom the Software is furnished to do so,
94 | subject to the following conditions:
95 |
96 | The above copyright notice and this permission notice shall be included in all
97 | copies or substantial portions of the Software.
98 |
99 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
100 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
101 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
102 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
103 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
104 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
105 |
106 |
--------------------------------------------------------------------------------
/WeatherApp/Controllers/CitiesViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // WeatherApp
4 | //
5 | // Created by Renzo Crisóstomo on 10/23/13.
6 | // Copyright (c) 2013 Renzo Crisóstomo. All rights reserved.
7 | //
8 |
9 | #import "CitiesViewController.h"
10 | #import "WeatherAPIManager.h"
11 | #import "City.h"
12 | #import "SearchViewController.h"
13 | #import "CityViewController.h"
14 |
15 | @interface CitiesViewController ()
16 | {
17 | NSMutableArray *_dataSource;
18 | CLLocationManager *_locationManager;
19 | __weak UIRefreshControl *_refreshControl;
20 | }
21 |
22 | - (void)setupAndStartLocationManager;
23 | - (void)setupRefreshControl;
24 | - (void)reloadDataSourceWithCities:(NSArray *)cities;
25 | - (void)notifyError;
26 | - (void)refreshLocation;
27 | - (void)endRefreshTableView;
28 |
29 | @end
30 |
31 | @implementation CitiesViewController
32 | {
33 | }
34 |
35 | #pragma mark - View Controller Lifecycle
36 |
37 | - (id)initWithCoder:(NSCoder *)aDecoder
38 | {
39 | self = [super initWithCoder:aDecoder];
40 | if (self) {
41 | _dataSource = [[NSMutableArray alloc] init];
42 | _locationManager = [[CLLocationManager alloc] init];
43 | }
44 | return self;
45 | }
46 |
47 | - (void)viewDidLoad
48 | {
49 | [super viewDidLoad];
50 | [self setupRefreshControl];
51 | [self setupAndStartLocationManager];
52 | }
53 |
54 | - (void)didReceiveMemoryWarning
55 | {
56 | [super didReceiveMemoryWarning];
57 | }
58 |
59 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
60 | {
61 | if ([[segue identifier] isEqualToString:@"CitySegue"]) {
62 | CityViewController *viewController = (CityViewController *)[segue destinationViewController];
63 | NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow];
64 | City *city = [_dataSource objectAtIndex:selectedIndexPath.row];
65 | viewController.city = city;
66 | }
67 | else if ([[segue identifier] isEqualToString:@"SearchSegue"]) {
68 | SearchViewController *viewController = (SearchViewController *)[segue destinationViewController];
69 | [viewController setDataSource:_dataSource];
70 | }
71 | }
72 |
73 | #pragma mark - Private Methods
74 |
75 | - (void)setupAndStartLocationManager
76 | {
77 | _locationManager.delegate = self;
78 | _locationManager.distanceFilter = kCLDistanceFilterNone;
79 | _locationManager.desiredAccuracy = kCLLocationAccuracyBest;
80 | [self refreshLocation];
81 | }
82 |
83 | - (void)setupRefreshControl
84 | {
85 | UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
86 | [refreshControl addTarget:self
87 | action:@selector(refreshLocation)
88 | forControlEvents:UIControlEventValueChanged];
89 | self.refreshControl = refreshControl;
90 | _refreshControl = refreshControl;
91 | }
92 |
93 | - (void)reloadDataSourceWithCities:(NSArray *)cities
94 | {
95 | [_dataSource removeAllObjects];
96 | [_dataSource addObjectsFromArray:cities];
97 | [self.tableView reloadData];
98 | }
99 |
100 | - (void)notifyError
101 | {
102 | UIAlertView *loadAlert = [[UIAlertView alloc] initWithTitle:@"Error"
103 | message:@"There was an error with the request"
104 | delegate:nil
105 | cancelButtonTitle:@"OK"
106 | otherButtonTitles:nil];
107 | [loadAlert show];
108 | }
109 |
110 | - (void)notifyAuthorization
111 | {
112 | UIAlertView *loadAlert = [[UIAlertView alloc] initWithTitle:@"Error"
113 | message:@"This application is not authorized for location "
114 | "services or location services are not enabled"
115 | delegate:nil
116 | cancelButtonTitle:@"OK"
117 | otherButtonTitles:nil];
118 | [loadAlert show];
119 | }
120 |
121 | - (void)refreshLocation
122 | {
123 | switch ([CLLocationManager authorizationStatus]) {
124 | case kCLAuthorizationStatusNotDetermined:
125 | [self endRefreshTableView];
126 | [_locationManager startUpdatingLocation];
127 | break;
128 | case kCLAuthorizationStatusRestricted:
129 | [self endRefreshTableView];
130 | [self notifyAuthorization];
131 | break;
132 | case kCLAuthorizationStatusDenied:
133 | [self endRefreshTableView];
134 | [self notifyAuthorization];
135 | break;
136 | case kCLAuthorizationStatusAuthorized:
137 | [_locationManager startUpdatingLocation];
138 | break;
139 | }
140 | }
141 |
142 | - (void)endRefreshTableView
143 | {
144 | if (_refreshControl.isRefreshing) {
145 | [_refreshControl endRefreshing];
146 | }
147 | }
148 |
149 | #pragma mark - CLLocationManager
150 |
151 | - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
152 | {
153 | [_locationManager stopUpdatingLocation];
154 | CLLocation *location = [locations lastObject];
155 | __weak typeof(self) blocksafeSelf = self;
156 | [[WeatherAPIManager sharedManager] citiesWithUserLatitude:[NSNumber numberWithDouble:location.coordinate.latitude]
157 | userLongitude:[NSNumber numberWithDouble:location.coordinate.longitude]
158 | andCallbackBlock:^(NSArray *cities, NSError *error) {
159 | [blocksafeSelf endRefreshTableView];
160 | if (error) {
161 | [blocksafeSelf notifyError];
162 | }
163 | else {
164 | [blocksafeSelf reloadDataSourceWithCities:cities];
165 | }
166 | }];
167 | }
168 |
169 | #pragma mark - UITableViewDataSource
170 |
171 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
172 | {
173 | return [_dataSource count];
174 | }
175 |
176 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
177 | {
178 | static NSString *CellIdentifier = @"CityCell";
179 | UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier
180 | forIndexPath:indexPath];
181 | City *city = nil;
182 | city = [_dataSource objectAtIndex:indexPath.row];
183 | cell.textLabel.text = city.name;
184 | cell.detailTextLabel.text = [NSString stringWithFormat:@"Lat: %.2f, Lon: %.2f",
185 | [city.latitude floatValue],[city.longitude floatValue]];
186 | return cell;
187 | }
188 |
189 | @end
190 |
--------------------------------------------------------------------------------
/WeatherAppTests/Tests/WeatherAPIManagerTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // WeatherAppTests.m
3 | // WeatherAppTests
4 | //
5 | // Created by Renzo Crisóstomo on 10/23/13.
6 | // Copyright (c) 2013 Renzo Crisóstomo. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #define HC_SHORTHAND
12 | #import
13 | #define MOCKITO_SHORTHAND
14 | #import
15 | #import "WeatherAPIManager.h"
16 | #import "TranslatorHelper.h"
17 | #import "ErrorNotificationHelper.h"
18 |
19 | @interface AFHTTPRequestOperationManagerSuccess : AFHTTPRequestOperationManager
20 |
21 | @property (nonatomic,strong) NSData *responseData;
22 |
23 | @end
24 |
25 | extern void __gcov_flush();
26 |
27 | @implementation AFHTTPRequestOperationManagerSuccess
28 |
29 | - (AFHTTPRequestOperation *)GET:(NSString *)URLString
30 | parameters:(NSDictionary *)parameters
31 | success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
32 | failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
33 | {
34 | success(nil, _responseData);
35 | return nil;
36 | }
37 |
38 | @end
39 |
40 | @interface AFHTTPRequestOperationManagerFailure : AFHTTPRequestOperationManager
41 |
42 | @property (nonatomic,strong) NSError *error;
43 |
44 | @end
45 |
46 | @implementation AFHTTPRequestOperationManagerFailure
47 |
48 | - (AFHTTPRequestOperation *)GET:(NSString *)URLString
49 | parameters:(NSDictionary *)parameters
50 | success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
51 | failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
52 | {
53 | failure(nil, _error);
54 | return nil;
55 | }
56 |
57 | @end
58 |
59 | @interface WeatherAPIManagerTests : XCTestCase
60 |
61 | @end
62 |
63 | @implementation WeatherAPIManagerTests
64 |
65 | - (void)setUp
66 | {
67 | [super setUp];
68 | }
69 |
70 | - (void)tearDown
71 | {
72 | WeatherAPIManager *weatherAPIManager = [WeatherAPIManager sharedManager];
73 | weatherAPIManager.errorNotificationHelper = nil;
74 | weatherAPIManager.translatorHelper = nil;
75 | __gcov_flush();
76 | [super tearDown];
77 | }
78 |
79 | #pragma mark - Property Tests
80 |
81 | - (void)testSharedManager
82 | {
83 | assertThat([WeatherAPIManager sharedManager], notNilValue());
84 | assertThat([WeatherAPIManager sharedManager], instanceOf([WeatherAPIManager class]));
85 | }
86 |
87 | - (void)testParserHelper
88 | {
89 | assertThat([[WeatherAPIManager sharedManager] translatorHelper], notNilValue());
90 | assertThat([[WeatherAPIManager sharedManager] translatorHelper], instanceOf([TranslatorHelper class]));
91 | }
92 |
93 | - (void)testErrorNotificationHelper
94 | {
95 | assertThat([[WeatherAPIManager sharedManager] errorNotificationHelper], notNilValue());
96 | assertThat([[WeatherAPIManager sharedManager] errorNotificationHelper], instanceOf([ErrorNotificationHelper class]));
97 | }
98 |
99 | #pragma mark - CitiesWithUserLatitudeUserLongitudeAndCallbackBlock Tests
100 |
101 | - (void)testCitiesWithUserLatitudeUserLongitudeAndCallbackBlockWithSuccessResponseAndValidResponseData
102 | {
103 | WeatherAPIManager *weatherAPIManager = [WeatherAPIManager sharedManager];
104 | AFHTTPRequestOperationManagerSuccess *requestOperationManagerSuccess = [[AFHTTPRequestOperationManagerSuccess alloc] init];
105 | NSData *responseData = [JSON_VALID_STRING dataUsingEncoding:NSUTF8StringEncoding];
106 | requestOperationManagerSuccess.responseData = responseData;
107 | weatherAPIManager.requestOperationManager = requestOperationManagerSuccess;
108 | TranslatorHelper *translatorHelper = mock([TranslatorHelper class]);
109 | weatherAPIManager.translatorHelper = translatorHelper;
110 | CallbackBlock callbackBlock = ^(NSArray *cities, NSError *error) {};
111 | [weatherAPIManager citiesWithUserLatitude:[NSNumber numberWithFloat:0.0f]
112 | userLongitude:[NSNumber numberWithFloat:0.0f]
113 | andCallbackBlock:callbackBlock];
114 | [verify(translatorHelper) parseCitiesWithResponseObject:responseData
115 | andCallbackBlock:callbackBlock];
116 | }
117 |
118 | - (void)testCitiesWithUserLatitudeUserLongitudeAndCallbackBlockWithSuccessResponseAndInvalidResponseData
119 | {
120 | WeatherAPIManager *weatherAPIManager = [WeatherAPIManager sharedManager];
121 | AFHTTPRequestOperationManagerSuccess *requestOperationManagerSuccess = [[AFHTTPRequestOperationManagerSuccess alloc] init];
122 | NSData *responseData = [JSON_INVALID_STRING dataUsingEncoding:NSUTF8StringEncoding];
123 | requestOperationManagerSuccess.responseData = responseData;
124 | weatherAPIManager.requestOperationManager = requestOperationManagerSuccess;
125 | TranslatorHelper *translatorHelper = mock([TranslatorHelper class]);
126 | weatherAPIManager.translatorHelper = translatorHelper;
127 | CallbackBlock callbackBlock = ^(NSArray *cities, NSError *error) {};
128 | [weatherAPIManager citiesWithUserLatitude:[NSNumber numberWithFloat:0.0f]
129 | userLongitude:[NSNumber numberWithFloat:0.0f]
130 | andCallbackBlock:callbackBlock];
131 | [verify(translatorHelper) parseCitiesWithResponseObject:responseData
132 | andCallbackBlock:callbackBlock];
133 | }
134 |
135 | - (void)testCitiesWithUserLatitudeUserLongitudeAndCallbackBlockWithFailureResponse
136 | {
137 | WeatherAPIManager *weatherAPIManager = [WeatherAPIManager sharedManager];
138 | AFHTTPRequestOperationManagerFailure *requestOperationManagerFailure = [[AFHTTPRequestOperationManagerFailure alloc] init];
139 | weatherAPIManager.requestOperationManager = requestOperationManagerFailure;
140 | ErrorNotificationHelper *errorNotificationHelper = mock([ErrorNotificationHelper class]);
141 | weatherAPIManager.errorNotificationHelper = errorNotificationHelper;
142 | CallbackBlock callbackBlock = ^(NSArray *cities, NSError *error) {};
143 | [weatherAPIManager citiesWithUserLatitude:[NSNumber numberWithFloat:0.0f]
144 | userLongitude:[NSNumber numberWithFloat:0.0f]
145 | andCallbackBlock:callbackBlock];
146 | [verify(errorNotificationHelper) notifyError:(NSError *)anything()
147 | withCallbackBlock:callbackBlock];
148 | }
149 |
150 | #pragma mark - ParseCitiesWithResponseObjectAndCallbackBlock Tests
151 |
152 | - (void)testParseCitiesWithResponseObjectAndCallbackBlockWithValidResponseObject
153 | {
154 | WeatherAPIManager *weatherAPIManager = [WeatherAPIManager sharedManager];
155 | AFHTTPRequestOperationManagerSuccess *requestOperationManagerSuccess = [[AFHTTPRequestOperationManagerSuccess alloc] init];
156 | requestOperationManagerSuccess.responseData = [JSON_VALID_STRING dataUsingEncoding:NSUTF8StringEncoding];
157 | weatherAPIManager.requestOperationManager = requestOperationManagerSuccess;
158 | id delegate = mockProtocol(@protocol(TranslatorHelperDelegate));
159 | weatherAPIManager.translatorHelper.delegate = delegate;
160 | CallbackBlock callbackBlock = ^(NSArray *cities, NSError *error) {};
161 | [weatherAPIManager citiesWithUserLatitude:[NSNumber numberWithFloat:0.0f]
162 | userLongitude:[NSNumber numberWithFloat:0.0f]
163 | andCallbackBlock:callbackBlock];
164 | [verify(delegate) didSucceedParseCities:(NSArray *)anything()
165 | withCallbackBlock:callbackBlock];
166 | }
167 |
168 | - (void)testParseCitiesWithResponseObjectAndCallbackBlockWithInvalidResponseObject
169 | {
170 | WeatherAPIManager *weatherAPIManager = [WeatherAPIManager sharedManager];
171 | AFHTTPRequestOperationManagerSuccess *requestOperationManagerSuccess = [[AFHTTPRequestOperationManagerSuccess alloc] init];
172 | requestOperationManagerSuccess.responseData = [JSON_INVALID_STRING dataUsingEncoding:NSUTF8StringEncoding];
173 | weatherAPIManager.requestOperationManager = requestOperationManagerSuccess;
174 | id delegate = mockProtocol(@protocol(TranslatorHelperDelegate));
175 | weatherAPIManager.translatorHelper.delegate = delegate;
176 | CallbackBlock callbackBlock = ^(NSArray *cities, NSError *error) {};
177 | [weatherAPIManager citiesWithUserLatitude:[NSNumber numberWithFloat:0.0f]
178 | userLongitude:[NSNumber numberWithFloat:0.0f]
179 | andCallbackBlock:callbackBlock];
180 | [verify(delegate) didFailParseCitiesWithError:(NSError *)anything()
181 | andCallbackBlock:callbackBlock];
182 | }
183 |
184 | @end
185 |
--------------------------------------------------------------------------------
/WeatherApp/Views/Storyboard.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
30 |
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 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
85 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
157 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
181 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
205 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
--------------------------------------------------------------------------------
/WeatherApp.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 3F68D14E18196E530025462E /* City.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F68D14D18196E520025462E /* City.m */; };
11 | 3F68D15118196FCC0025462E /* ErrorNotificationHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F68D15018196FCC0025462E /* ErrorNotificationHelper.m */; };
12 | 3F68D1571819781D0025462E /* TranslatorHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F68D1561819781D0025462E /* TranslatorHelper.m */; };
13 | 3F68D15A18197A420025462E /* WeatherAPIManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F68D15918197A420025462E /* WeatherAPIManager.m */; };
14 | 3F68D15D181988D60025462E /* ValidatorHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F68D15C181988D60025462E /* ValidatorHelper.m */; };
15 | 3F68D15E1819968A0025462E /* ErrorNotificationHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F68D15018196FCC0025462E /* ErrorNotificationHelper.m */; };
16 | 3F68D15F1819968A0025462E /* TranslatorHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F68D1561819781D0025462E /* TranslatorHelper.m */; };
17 | 3F68D1601819968A0025462E /* ValidatorHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F68D15C181988D60025462E /* ValidatorHelper.m */; };
18 | 3F68D1611819968A0025462E /* WeatherAPIManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F68D15918197A420025462E /* WeatherAPIManager.m */; };
19 | 3F68D1621819968A0025462E /* City.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F68D14D18196E520025462E /* City.m */; };
20 | 3F68D16718199F0A0025462E /* CityTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F68D16618199F0A0025462E /* CityTests.m */; };
21 | 3F7658FC18274E190004CB17 /* CityViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F7658FB18274E190004CB17 /* CityViewController.m */; };
22 | 3F7658FF18274E340004CB17 /* SearchViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F7658FE18274E340004CB17 /* SearchViewController.m */; };
23 | 3F793A02181875DC00E217D6 /* Storyboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3F793A01181875DC00E217D6 /* Storyboard.storyboard */; };
24 | 3F793A061818768600E217D6 /* WeatherAPIManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F793A051818768600E217D6 /* WeatherAPIManagerTests.m */; };
25 | 3F81F20318187071008C9510 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F81F20218187071008C9510 /* Foundation.framework */; };
26 | 3F81F20518187071008C9510 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F81F20418187071008C9510 /* CoreGraphics.framework */; };
27 | 3F81F20718187071008C9510 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F81F20618187071008C9510 /* UIKit.framework */; };
28 | 3F81F22218187072008C9510 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F81F22118187072008C9510 /* XCTest.framework */; };
29 | 3F81F22318187072008C9510 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F81F20218187071008C9510 /* Foundation.framework */; };
30 | 3F81F22418187072008C9510 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F81F20618187071008C9510 /* UIKit.framework */; };
31 | 3F81F24018187210008C9510 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F81F23F18187210008C9510 /* AppDelegate.m */; };
32 | 3F81F2431818721B008C9510 /* CitiesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F81F2421818721B008C9510 /* CitiesViewController.m */; };
33 | 3F81F2491818722A008C9510 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3F81F2441818722A008C9510 /* Images.xcassets */; };
34 | 3F81F24B1818722A008C9510 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F81F2461818722A008C9510 /* main.m */; };
35 | 4267E7451BF0422F9795EB0C /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A47FB8CEF936484FBEECF34F /* libPods.a */; };
36 | 73D0D0122F374EBF803EE690 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A47FB8CEF936484FBEECF34F /* libPods.a */; };
37 | 94831261BCCC4F8C88E06E3C /* libPods-WeatherAppTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AEA04102603245F7B44AA29F /* libPods-WeatherAppTests.a */; };
38 | /* End PBXBuildFile section */
39 |
40 | /* Begin PBXContainerItemProxy section */
41 | 3F81F22518187072008C9510 /* PBXContainerItemProxy */ = {
42 | isa = PBXContainerItemProxy;
43 | containerPortal = 3F81F1F718187071008C9510 /* Project object */;
44 | proxyType = 1;
45 | remoteGlobalIDString = 3F81F1FE18187071008C9510;
46 | remoteInfo = WeatherApp;
47 | };
48 | /* End PBXContainerItemProxy section */
49 |
50 | /* Begin PBXFileReference section */
51 | 27112ADA5406462D987E6824 /* Pods.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.xcconfig; path = Pods/Pods.xcconfig; sourceTree = ""; };
52 | 3F68D14C18196E510025462E /* City.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = City.h; path = Models/City.h; sourceTree = ""; };
53 | 3F68D14D18196E520025462E /* City.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = City.m; path = Models/City.m; sourceTree = ""; };
54 | 3F68D14F18196FCC0025462E /* ErrorNotificationHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ErrorNotificationHelper.h; path = Helpers/ErrorNotificationHelper.h; sourceTree = ""; };
55 | 3F68D15018196FCC0025462E /* ErrorNotificationHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ErrorNotificationHelper.m; path = Helpers/ErrorNotificationHelper.m; sourceTree = ""; };
56 | 3F68D154181976D70025462E /* Includes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Includes.h; path = Support/Includes.h; sourceTree = ""; };
57 | 3F68D1551819781D0025462E /* TranslatorHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TranslatorHelper.h; path = Helpers/TranslatorHelper.h; sourceTree = ""; };
58 | 3F68D1561819781D0025462E /* TranslatorHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TranslatorHelper.m; path = Helpers/TranslatorHelper.m; sourceTree = ""; };
59 | 3F68D15818197A420025462E /* WeatherAPIManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WeatherAPIManager.h; path = Managers/WeatherAPIManager.h; sourceTree = ""; };
60 | 3F68D15918197A420025462E /* WeatherAPIManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WeatherAPIManager.m; path = Managers/WeatherAPIManager.m; sourceTree = ""; };
61 | 3F68D15B181988D60025462E /* ValidatorHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ValidatorHelper.h; path = Helpers/ValidatorHelper.h; sourceTree = ""; };
62 | 3F68D15C181988D60025462E /* ValidatorHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ValidatorHelper.m; path = Helpers/ValidatorHelper.m; sourceTree = ""; };
63 | 3F68D16618199F0A0025462E /* CityTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CityTests.m; path = Tests/CityTests.m; sourceTree = ""; };
64 | 3F7658FA18274E190004CB17 /* CityViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CityViewController.h; path = Controllers/CityViewController.h; sourceTree = ""; };
65 | 3F7658FB18274E190004CB17 /* CityViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CityViewController.m; path = Controllers/CityViewController.m; sourceTree = ""; };
66 | 3F7658FD18274E340004CB17 /* SearchViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SearchViewController.h; path = Controllers/SearchViewController.h; sourceTree = ""; };
67 | 3F7658FE18274E340004CB17 /* SearchViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SearchViewController.m; path = Controllers/SearchViewController.m; sourceTree = ""; };
68 | 3F793A01181875DC00E217D6 /* Storyboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Storyboard.storyboard; path = Views/Storyboard.storyboard; sourceTree = ""; };
69 | 3F793A051818768600E217D6 /* WeatherAPIManagerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WeatherAPIManagerTests.m; path = Tests/WeatherAPIManagerTests.m; sourceTree = ""; };
70 | 3F793A071818768C00E217D6 /* InfoPlist.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = InfoPlist.strings; path = Support/InfoPlist.strings; sourceTree = ""; };
71 | 3F793A081818768C00E217D6 /* WeatherAppTests-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "WeatherAppTests-Info.plist"; path = "Support/WeatherAppTests-Info.plist"; sourceTree = ""; };
72 | 3F81F1FF18187071008C9510 /* WeatherApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WeatherApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
73 | 3F81F20218187071008C9510 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
74 | 3F81F20418187071008C9510 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
75 | 3F81F20618187071008C9510 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
76 | 3F81F22018187072008C9510 /* WeatherAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WeatherAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
77 | 3F81F22118187072008C9510 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
78 | 3F81F23E18187210008C9510 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Application/AppDelegate.h; sourceTree = ""; };
79 | 3F81F23F18187210008C9510 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = Application/AppDelegate.m; sourceTree = ""; };
80 | 3F81F2411818721B008C9510 /* CitiesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CitiesViewController.h; path = Controllers/CitiesViewController.h; sourceTree = ""; };
81 | 3F81F2421818721B008C9510 /* CitiesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CitiesViewController.m; path = Controllers/CitiesViewController.m; sourceTree = ""; };
82 | 3F81F2441818722A008C9510 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Support/Images.xcassets; sourceTree = ""; };
83 | 3F81F2461818722A008C9510 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Support/main.m; sourceTree = ""; };
84 | 3F81F2471818722A008C9510 /* WeatherApp-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "WeatherApp-Info.plist"; path = "Support/WeatherApp-Info.plist"; sourceTree = ""; };
85 | 3F81F2481818722A008C9510 /* WeatherApp-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "WeatherApp-Prefix.pch"; path = "Support/WeatherApp-Prefix.pch"; sourceTree = ""; };
86 | 3F81F24E18187231008C9510 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = ""; };
87 | A0A7F38C05814B88B8169146 /* Pods-WeatherAppTests.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WeatherAppTests.xcconfig"; path = "Pods/Pods-WeatherAppTests.xcconfig"; sourceTree = ""; };
88 | A47FB8CEF936484FBEECF34F /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
89 | AEA04102603245F7B44AA29F /* libPods-WeatherAppTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-WeatherAppTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
90 | /* End PBXFileReference section */
91 |
92 | /* Begin PBXFrameworksBuildPhase section */
93 | 3F81F1FC18187071008C9510 /* Frameworks */ = {
94 | isa = PBXFrameworksBuildPhase;
95 | buildActionMask = 2147483647;
96 | files = (
97 | 3F81F20518187071008C9510 /* CoreGraphics.framework in Frameworks */,
98 | 3F81F20718187071008C9510 /* UIKit.framework in Frameworks */,
99 | 3F81F20318187071008C9510 /* Foundation.framework in Frameworks */,
100 | 73D0D0122F374EBF803EE690 /* libPods.a in Frameworks */,
101 | );
102 | runOnlyForDeploymentPostprocessing = 0;
103 | };
104 | 3F81F21D18187072008C9510 /* Frameworks */ = {
105 | isa = PBXFrameworksBuildPhase;
106 | buildActionMask = 2147483647;
107 | files = (
108 | 3F81F22218187072008C9510 /* XCTest.framework in Frameworks */,
109 | 3F81F22418187072008C9510 /* UIKit.framework in Frameworks */,
110 | 3F81F22318187072008C9510 /* Foundation.framework in Frameworks */,
111 | 4267E7451BF0422F9795EB0C /* libPods.a in Frameworks */,
112 | 94831261BCCC4F8C88E06E3C /* libPods-WeatherAppTests.a in Frameworks */,
113 | );
114 | runOnlyForDeploymentPostprocessing = 0;
115 | };
116 | /* End PBXFrameworksBuildPhase section */
117 |
118 | /* Begin PBXGroup section */
119 | 3F793A031818767100E217D6 /* Tests */ = {
120 | isa = PBXGroup;
121 | children = (
122 | 3F793A051818768600E217D6 /* WeatherAPIManagerTests.m */,
123 | 3F68D16618199F0A0025462E /* CityTests.m */,
124 | );
125 | name = Tests;
126 | sourceTree = "";
127 | };
128 | 3F793A041818767700E217D6 /* Support */ = {
129 | isa = PBXGroup;
130 | children = (
131 | 3F793A071818768C00E217D6 /* InfoPlist.strings */,
132 | 3F793A081818768C00E217D6 /* WeatherAppTests-Info.plist */,
133 | );
134 | name = Support;
135 | sourceTree = "";
136 | };
137 | 3F81F1F618187071008C9510 = {
138 | isa = PBXGroup;
139 | children = (
140 | 3F81F20818187071008C9510 /* WeatherApp */,
141 | 3F81F22718187072008C9510 /* WeatherAppTests */,
142 | 3F81F20118187071008C9510 /* Frameworks */,
143 | 3F81F20018187071008C9510 /* Products */,
144 | 27112ADA5406462D987E6824 /* Pods.xcconfig */,
145 | A0A7F38C05814B88B8169146 /* Pods-WeatherAppTests.xcconfig */,
146 | );
147 | sourceTree = "";
148 | };
149 | 3F81F20018187071008C9510 /* Products */ = {
150 | isa = PBXGroup;
151 | children = (
152 | 3F81F1FF18187071008C9510 /* WeatherApp.app */,
153 | 3F81F22018187072008C9510 /* WeatherAppTests.xctest */,
154 | );
155 | name = Products;
156 | sourceTree = "";
157 | };
158 | 3F81F20118187071008C9510 /* Frameworks */ = {
159 | isa = PBXGroup;
160 | children = (
161 | 3F81F20218187071008C9510 /* Foundation.framework */,
162 | 3F81F20418187071008C9510 /* CoreGraphics.framework */,
163 | 3F81F20618187071008C9510 /* UIKit.framework */,
164 | 3F81F22118187072008C9510 /* XCTest.framework */,
165 | A47FB8CEF936484FBEECF34F /* libPods.a */,
166 | AEA04102603245F7B44AA29F /* libPods-WeatherAppTests.a */,
167 | );
168 | name = Frameworks;
169 | sourceTree = "";
170 | };
171 | 3F81F20818187071008C9510 /* WeatherApp */ = {
172 | isa = PBXGroup;
173 | children = (
174 | 3F81F237181871A2008C9510 /* Application */,
175 | 3F81F23B181871C6008C9510 /* Controllers */,
176 | 3F81F238181871A9008C9510 /* Helpers */,
177 | 3F81F23C181871CD008C9510 /* Managers */,
178 | 3F81F239181871B7008C9510 /* Models */,
179 | 3F81F23D181871D4008C9510 /* Support */,
180 | 3F81F23A181871C0008C9510 /* Views */,
181 | );
182 | path = WeatherApp;
183 | sourceTree = "";
184 | };
185 | 3F81F22718187072008C9510 /* WeatherAppTests */ = {
186 | isa = PBXGroup;
187 | children = (
188 | 3F793A031818767100E217D6 /* Tests */,
189 | 3F793A041818767700E217D6 /* Support */,
190 | );
191 | path = WeatherAppTests;
192 | sourceTree = "";
193 | };
194 | 3F81F237181871A2008C9510 /* Application */ = {
195 | isa = PBXGroup;
196 | children = (
197 | 3F81F23E18187210008C9510 /* AppDelegate.h */,
198 | 3F81F23F18187210008C9510 /* AppDelegate.m */,
199 | );
200 | name = Application;
201 | sourceTree = "";
202 | };
203 | 3F81F238181871A9008C9510 /* Helpers */ = {
204 | isa = PBXGroup;
205 | children = (
206 | 3F68D14F18196FCC0025462E /* ErrorNotificationHelper.h */,
207 | 3F68D15018196FCC0025462E /* ErrorNotificationHelper.m */,
208 | 3F68D1551819781D0025462E /* TranslatorHelper.h */,
209 | 3F68D1561819781D0025462E /* TranslatorHelper.m */,
210 | 3F68D15B181988D60025462E /* ValidatorHelper.h */,
211 | 3F68D15C181988D60025462E /* ValidatorHelper.m */,
212 | );
213 | name = Helpers;
214 | sourceTree = "";
215 | };
216 | 3F81F239181871B7008C9510 /* Models */ = {
217 | isa = PBXGroup;
218 | children = (
219 | 3F68D14C18196E510025462E /* City.h */,
220 | 3F68D14D18196E520025462E /* City.m */,
221 | );
222 | name = Models;
223 | sourceTree = "";
224 | };
225 | 3F81F23A181871C0008C9510 /* Views */ = {
226 | isa = PBXGroup;
227 | children = (
228 | 3F793A01181875DC00E217D6 /* Storyboard.storyboard */,
229 | );
230 | name = Views;
231 | sourceTree = "";
232 | };
233 | 3F81F23B181871C6008C9510 /* Controllers */ = {
234 | isa = PBXGroup;
235 | children = (
236 | 3F81F2411818721B008C9510 /* CitiesViewController.h */,
237 | 3F81F2421818721B008C9510 /* CitiesViewController.m */,
238 | 3F7658FA18274E190004CB17 /* CityViewController.h */,
239 | 3F7658FB18274E190004CB17 /* CityViewController.m */,
240 | 3F7658FD18274E340004CB17 /* SearchViewController.h */,
241 | 3F7658FE18274E340004CB17 /* SearchViewController.m */,
242 | );
243 | name = Controllers;
244 | sourceTree = "";
245 | };
246 | 3F81F23C181871CD008C9510 /* Managers */ = {
247 | isa = PBXGroup;
248 | children = (
249 | 3F68D15818197A420025462E /* WeatherAPIManager.h */,
250 | 3F68D15918197A420025462E /* WeatherAPIManager.m */,
251 | );
252 | name = Managers;
253 | sourceTree = "";
254 | };
255 | 3F81F23D181871D4008C9510 /* Support */ = {
256 | isa = PBXGroup;
257 | children = (
258 | 3F68D154181976D70025462E /* Includes.h */,
259 | 3F81F2441818722A008C9510 /* Images.xcassets */,
260 | 3F81F24F18187231008C9510 /* InfoPlist.strings */,
261 | 3F81F2461818722A008C9510 /* main.m */,
262 | 3F81F2471818722A008C9510 /* WeatherApp-Info.plist */,
263 | 3F81F2481818722A008C9510 /* WeatherApp-Prefix.pch */,
264 | );
265 | name = Support;
266 | sourceTree = "";
267 | };
268 | /* End PBXGroup section */
269 |
270 | /* Begin PBXNativeTarget section */
271 | 3F81F1FE18187071008C9510 /* WeatherApp */ = {
272 | isa = PBXNativeTarget;
273 | buildConfigurationList = 3F81F23118187072008C9510 /* Build configuration list for PBXNativeTarget "WeatherApp" */;
274 | buildPhases = (
275 | 7359B2D29C8B4C448FFCE961 /* Check Pods Manifest.lock */,
276 | 3F81F1FB18187071008C9510 /* Sources */,
277 | 3F81F1FC18187071008C9510 /* Frameworks */,
278 | 3F81F1FD18187071008C9510 /* Resources */,
279 | 08DA442E73C74260BE9A0276 /* Copy Pods Resources */,
280 | );
281 | buildRules = (
282 | );
283 | dependencies = (
284 | );
285 | name = WeatherApp;
286 | productName = WeatherApp;
287 | productReference = 3F81F1FF18187071008C9510 /* WeatherApp.app */;
288 | productType = "com.apple.product-type.application";
289 | };
290 | 3F81F21F18187072008C9510 /* WeatherAppTests */ = {
291 | isa = PBXNativeTarget;
292 | buildConfigurationList = 3F81F23418187072008C9510 /* Build configuration list for PBXNativeTarget "WeatherAppTests" */;
293 | buildPhases = (
294 | 48D6787C0D8D4F899B8571CF /* Check Pods Manifest.lock */,
295 | 3F81F21C18187072008C9510 /* Sources */,
296 | 3F81F21D18187072008C9510 /* Frameworks */,
297 | 3F81F21E18187072008C9510 /* Resources */,
298 | 64F597BE1A6C4161984646B3 /* Copy Pods Resources */,
299 | );
300 | buildRules = (
301 | );
302 | dependencies = (
303 | 3F81F22618187072008C9510 /* PBXTargetDependency */,
304 | );
305 | name = WeatherAppTests;
306 | productName = WeatherAppTests;
307 | productReference = 3F81F22018187072008C9510 /* WeatherAppTests.xctest */;
308 | productType = "com.apple.product-type.bundle.unit-test";
309 | };
310 | /* End PBXNativeTarget section */
311 |
312 | /* Begin PBXProject section */
313 | 3F81F1F718187071008C9510 /* Project object */ = {
314 | isa = PBXProject;
315 | attributes = {
316 | LastUpgradeCheck = 0500;
317 | ORGANIZATIONNAME = "Renzo Crisóstomo";
318 | TargetAttributes = {
319 | 3F81F21F18187072008C9510 = {
320 | TestTargetID = 3F81F1FE18187071008C9510;
321 | };
322 | };
323 | };
324 | buildConfigurationList = 3F81F1FA18187071008C9510 /* Build configuration list for PBXProject "WeatherApp" */;
325 | compatibilityVersion = "Xcode 3.2";
326 | developmentRegion = English;
327 | hasScannedForEncodings = 0;
328 | knownRegions = (
329 | en,
330 | Base,
331 | );
332 | mainGroup = 3F81F1F618187071008C9510;
333 | productRefGroup = 3F81F20018187071008C9510 /* Products */;
334 | projectDirPath = "";
335 | projectRoot = "";
336 | targets = (
337 | 3F81F1FE18187071008C9510 /* WeatherApp */,
338 | 3F81F21F18187072008C9510 /* WeatherAppTests */,
339 | );
340 | };
341 | /* End PBXProject section */
342 |
343 | /* Begin PBXResourcesBuildPhase section */
344 | 3F81F1FD18187071008C9510 /* Resources */ = {
345 | isa = PBXResourcesBuildPhase;
346 | buildActionMask = 2147483647;
347 | files = (
348 | 3F81F2491818722A008C9510 /* Images.xcassets in Resources */,
349 | 3F793A02181875DC00E217D6 /* Storyboard.storyboard in Resources */,
350 | );
351 | runOnlyForDeploymentPostprocessing = 0;
352 | };
353 | 3F81F21E18187072008C9510 /* Resources */ = {
354 | isa = PBXResourcesBuildPhase;
355 | buildActionMask = 2147483647;
356 | files = (
357 | );
358 | runOnlyForDeploymentPostprocessing = 0;
359 | };
360 | /* End PBXResourcesBuildPhase section */
361 |
362 | /* Begin PBXShellScriptBuildPhase section */
363 | 08DA442E73C74260BE9A0276 /* Copy Pods Resources */ = {
364 | isa = PBXShellScriptBuildPhase;
365 | buildActionMask = 2147483647;
366 | files = (
367 | );
368 | inputPaths = (
369 | );
370 | name = "Copy Pods Resources";
371 | outputPaths = (
372 | );
373 | runOnlyForDeploymentPostprocessing = 0;
374 | shellPath = /bin/sh;
375 | shellScript = "\"${SRCROOT}/Pods/Pods-resources.sh\"\n";
376 | showEnvVarsInLog = 0;
377 | };
378 | 48D6787C0D8D4F899B8571CF /* Check Pods Manifest.lock */ = {
379 | isa = PBXShellScriptBuildPhase;
380 | buildActionMask = 2147483647;
381 | files = (
382 | );
383 | inputPaths = (
384 | );
385 | name = "Check Pods Manifest.lock";
386 | outputPaths = (
387 | );
388 | runOnlyForDeploymentPostprocessing = 0;
389 | shellPath = /bin/sh;
390 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
391 | showEnvVarsInLog = 0;
392 | };
393 | 64F597BE1A6C4161984646B3 /* Copy Pods Resources */ = {
394 | isa = PBXShellScriptBuildPhase;
395 | buildActionMask = 2147483647;
396 | files = (
397 | );
398 | inputPaths = (
399 | );
400 | name = "Copy Pods Resources";
401 | outputPaths = (
402 | );
403 | runOnlyForDeploymentPostprocessing = 0;
404 | shellPath = /bin/sh;
405 | shellScript = "\"${SRCROOT}/Pods/Pods-WeatherAppTests-resources.sh\"\n";
406 | showEnvVarsInLog = 0;
407 | };
408 | 7359B2D29C8B4C448FFCE961 /* Check Pods Manifest.lock */ = {
409 | isa = PBXShellScriptBuildPhase;
410 | buildActionMask = 2147483647;
411 | files = (
412 | );
413 | inputPaths = (
414 | );
415 | name = "Check Pods Manifest.lock";
416 | outputPaths = (
417 | );
418 | runOnlyForDeploymentPostprocessing = 0;
419 | shellPath = /bin/sh;
420 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
421 | showEnvVarsInLog = 0;
422 | };
423 | /* End PBXShellScriptBuildPhase section */
424 |
425 | /* Begin PBXSourcesBuildPhase section */
426 | 3F81F1FB18187071008C9510 /* Sources */ = {
427 | isa = PBXSourcesBuildPhase;
428 | buildActionMask = 2147483647;
429 | files = (
430 | 3F81F2431818721B008C9510 /* CitiesViewController.m in Sources */,
431 | 3F68D1571819781D0025462E /* TranslatorHelper.m in Sources */,
432 | 3F7658FF18274E340004CB17 /* SearchViewController.m in Sources */,
433 | 3F68D15D181988D60025462E /* ValidatorHelper.m in Sources */,
434 | 3F68D15A18197A420025462E /* WeatherAPIManager.m in Sources */,
435 | 3F81F24B1818722A008C9510 /* main.m in Sources */,
436 | 3F68D14E18196E530025462E /* City.m in Sources */,
437 | 3F7658FC18274E190004CB17 /* CityViewController.m in Sources */,
438 | 3F81F24018187210008C9510 /* AppDelegate.m in Sources */,
439 | 3F68D15118196FCC0025462E /* ErrorNotificationHelper.m in Sources */,
440 | );
441 | runOnlyForDeploymentPostprocessing = 0;
442 | };
443 | 3F81F21C18187072008C9510 /* Sources */ = {
444 | isa = PBXSourcesBuildPhase;
445 | buildActionMask = 2147483647;
446 | files = (
447 | 3F68D15E1819968A0025462E /* ErrorNotificationHelper.m in Sources */,
448 | 3F68D15F1819968A0025462E /* TranslatorHelper.m in Sources */,
449 | 3F68D1601819968A0025462E /* ValidatorHelper.m in Sources */,
450 | 3F68D1611819968A0025462E /* WeatherAPIManager.m in Sources */,
451 | 3F68D1621819968A0025462E /* City.m in Sources */,
452 | 3F793A061818768600E217D6 /* WeatherAPIManagerTests.m in Sources */,
453 | 3F68D16718199F0A0025462E /* CityTests.m in Sources */,
454 | );
455 | runOnlyForDeploymentPostprocessing = 0;
456 | };
457 | /* End PBXSourcesBuildPhase section */
458 |
459 | /* Begin PBXTargetDependency section */
460 | 3F81F22618187072008C9510 /* PBXTargetDependency */ = {
461 | isa = PBXTargetDependency;
462 | target = 3F81F1FE18187071008C9510 /* WeatherApp */;
463 | targetProxy = 3F81F22518187072008C9510 /* PBXContainerItemProxy */;
464 | };
465 | /* End PBXTargetDependency section */
466 |
467 | /* Begin PBXVariantGroup section */
468 | 3F81F24F18187231008C9510 /* InfoPlist.strings */ = {
469 | isa = PBXVariantGroup;
470 | children = (
471 | 3F81F24E18187231008C9510 /* Base */,
472 | );
473 | name = InfoPlist.strings;
474 | path = Support;
475 | sourceTree = "";
476 | };
477 | /* End PBXVariantGroup section */
478 |
479 | /* Begin XCBuildConfiguration section */
480 | 3F81F22F18187072008C9510 /* Debug */ = {
481 | isa = XCBuildConfiguration;
482 | buildSettings = {
483 | ALWAYS_SEARCH_USER_PATHS = NO;
484 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
485 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
486 | CLANG_CXX_LIBRARY = "libc++";
487 | CLANG_ENABLE_MODULES = YES;
488 | CLANG_ENABLE_OBJC_ARC = YES;
489 | CLANG_WARN_BOOL_CONVERSION = YES;
490 | CLANG_WARN_CONSTANT_CONVERSION = YES;
491 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
492 | CLANG_WARN_EMPTY_BODY = YES;
493 | CLANG_WARN_ENUM_CONVERSION = YES;
494 | CLANG_WARN_INT_CONVERSION = YES;
495 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
496 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
497 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
498 | COPY_PHASE_STRIP = NO;
499 | GCC_C_LANGUAGE_STANDARD = gnu99;
500 | GCC_DYNAMIC_NO_PIC = NO;
501 | GCC_OPTIMIZATION_LEVEL = 0;
502 | GCC_PREPROCESSOR_DEFINITIONS = (
503 | "DEBUG=1",
504 | "$(inherited)",
505 | );
506 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
507 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
508 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
509 | GCC_WARN_UNDECLARED_SELECTOR = YES;
510 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
511 | GCC_WARN_UNUSED_FUNCTION = YES;
512 | GCC_WARN_UNUSED_VARIABLE = YES;
513 | IPHONEOS_DEPLOYMENT_TARGET = 7.0;
514 | ONLY_ACTIVE_ARCH = YES;
515 | SDKROOT = iphoneos;
516 | };
517 | name = Debug;
518 | };
519 | 3F81F23018187072008C9510 /* Release */ = {
520 | isa = XCBuildConfiguration;
521 | buildSettings = {
522 | ALWAYS_SEARCH_USER_PATHS = NO;
523 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
524 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
525 | CLANG_CXX_LIBRARY = "libc++";
526 | CLANG_ENABLE_MODULES = YES;
527 | CLANG_ENABLE_OBJC_ARC = YES;
528 | CLANG_WARN_BOOL_CONVERSION = YES;
529 | CLANG_WARN_CONSTANT_CONVERSION = YES;
530 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
531 | CLANG_WARN_EMPTY_BODY = YES;
532 | CLANG_WARN_ENUM_CONVERSION = YES;
533 | CLANG_WARN_INT_CONVERSION = YES;
534 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
535 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
536 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
537 | COPY_PHASE_STRIP = YES;
538 | ENABLE_NS_ASSERTIONS = NO;
539 | GCC_C_LANGUAGE_STANDARD = gnu99;
540 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
541 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
542 | GCC_WARN_UNDECLARED_SELECTOR = YES;
543 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
544 | GCC_WARN_UNUSED_FUNCTION = YES;
545 | GCC_WARN_UNUSED_VARIABLE = YES;
546 | IPHONEOS_DEPLOYMENT_TARGET = 7.0;
547 | SDKROOT = iphoneos;
548 | VALIDATE_PRODUCT = YES;
549 | };
550 | name = Release;
551 | };
552 | 3F81F23218187072008C9510 /* Debug */ = {
553 | isa = XCBuildConfiguration;
554 | baseConfigurationReference = 27112ADA5406462D987E6824 /* Pods.xcconfig */;
555 | buildSettings = {
556 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
557 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
558 | GCC_GENERATE_TEST_COVERAGE_FILES = NO;
559 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
560 | GCC_PREFIX_HEADER = "WeatherApp/Support/WeatherApp-Prefix.pch";
561 | INFOPLIST_FILE = "WeatherApp/Support/WeatherApp-Info.plist";
562 | PRODUCT_NAME = "$(TARGET_NAME)";
563 | WRAPPER_EXTENSION = app;
564 | };
565 | name = Debug;
566 | };
567 | 3F81F23318187072008C9510 /* Release */ = {
568 | isa = XCBuildConfiguration;
569 | baseConfigurationReference = 27112ADA5406462D987E6824 /* Pods.xcconfig */;
570 | buildSettings = {
571 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
572 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
573 | GCC_GENERATE_TEST_COVERAGE_FILES = NO;
574 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
575 | GCC_PREFIX_HEADER = "WeatherApp/Support/WeatherApp-Prefix.pch";
576 | INFOPLIST_FILE = "WeatherApp/Support/WeatherApp-Info.plist";
577 | PRODUCT_NAME = "$(TARGET_NAME)";
578 | WRAPPER_EXTENSION = app;
579 | };
580 | name = Release;
581 | };
582 | 3F81F23518187072008C9510 /* Debug */ = {
583 | isa = XCBuildConfiguration;
584 | baseConfigurationReference = A0A7F38C05814B88B8169146 /* Pods-WeatherAppTests.xcconfig */;
585 | buildSettings = {
586 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
587 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/WeatherApp.app/WeatherApp";
588 | CLANG_ENABLE_MODULES = NO;
589 | FRAMEWORK_SEARCH_PATHS = (
590 | "$(SDKROOT)/Developer/Library/Frameworks",
591 | "$(inherited)",
592 | "$(DEVELOPER_FRAMEWORKS_DIR)",
593 | );
594 | GCC_GENERATE_TEST_COVERAGE_FILES = YES;
595 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
596 | GCC_PREFIX_HEADER = "WeatherApp/Support/WeatherApp-Prefix.pch";
597 | GCC_PREPROCESSOR_DEFINITIONS = (
598 | "DEBUG=1",
599 | "$(inherited)",
600 | );
601 | INFOPLIST_FILE = "WeatherAppTests/Support/WeatherAppTests-Info.plist";
602 | PRODUCT_NAME = "$(TARGET_NAME)";
603 | TEST_HOST = "$(BUNDLE_LOADER)";
604 | WRAPPER_EXTENSION = xctest;
605 | };
606 | name = Debug;
607 | };
608 | 3F81F23618187072008C9510 /* Release */ = {
609 | isa = XCBuildConfiguration;
610 | baseConfigurationReference = A0A7F38C05814B88B8169146 /* Pods-WeatherAppTests.xcconfig */;
611 | buildSettings = {
612 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
613 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/WeatherApp.app/WeatherApp";
614 | CLANG_ENABLE_MODULES = NO;
615 | FRAMEWORK_SEARCH_PATHS = (
616 | "$(SDKROOT)/Developer/Library/Frameworks",
617 | "$(inherited)",
618 | "$(DEVELOPER_FRAMEWORKS_DIR)",
619 | );
620 | GCC_GENERATE_TEST_COVERAGE_FILES = YES;
621 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
622 | GCC_PREFIX_HEADER = "WeatherApp/Support/WeatherApp-Prefix.pch";
623 | INFOPLIST_FILE = "WeatherAppTests/Support/WeatherAppTests-Info.plist";
624 | PRODUCT_NAME = "$(TARGET_NAME)";
625 | TEST_HOST = "$(BUNDLE_LOADER)";
626 | WRAPPER_EXTENSION = xctest;
627 | };
628 | name = Release;
629 | };
630 | 3F9D7F0C181A15C400CD7CCF /* Coverage */ = {
631 | isa = XCBuildConfiguration;
632 | buildSettings = {
633 | ALWAYS_SEARCH_USER_PATHS = NO;
634 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
635 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
636 | CLANG_CXX_LIBRARY = "libc++";
637 | CLANG_ENABLE_MODULES = YES;
638 | CLANG_ENABLE_OBJC_ARC = YES;
639 | CLANG_WARN_BOOL_CONVERSION = YES;
640 | CLANG_WARN_CONSTANT_CONVERSION = YES;
641 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
642 | CLANG_WARN_EMPTY_BODY = YES;
643 | CLANG_WARN_ENUM_CONVERSION = YES;
644 | CLANG_WARN_INT_CONVERSION = YES;
645 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
646 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
647 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
648 | COPY_PHASE_STRIP = NO;
649 | GCC_C_LANGUAGE_STANDARD = gnu99;
650 | GCC_DYNAMIC_NO_PIC = NO;
651 | GCC_OPTIMIZATION_LEVEL = 0;
652 | GCC_PREPROCESSOR_DEFINITIONS = (
653 | "DEBUG=1",
654 | "$(inherited)",
655 | );
656 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
657 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
658 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
659 | GCC_WARN_UNDECLARED_SELECTOR = YES;
660 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
661 | GCC_WARN_UNUSED_FUNCTION = YES;
662 | GCC_WARN_UNUSED_VARIABLE = YES;
663 | IPHONEOS_DEPLOYMENT_TARGET = 7.0;
664 | ONLY_ACTIVE_ARCH = YES;
665 | SDKROOT = iphoneos;
666 | };
667 | name = Coverage;
668 | };
669 | 3F9D7F0D181A15C400CD7CCF /* Coverage */ = {
670 | isa = XCBuildConfiguration;
671 | baseConfigurationReference = 27112ADA5406462D987E6824 /* Pods.xcconfig */;
672 | buildSettings = {
673 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
674 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
675 | GCC_GENERATE_TEST_COVERAGE_FILES = YES;
676 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
677 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
678 | GCC_PREFIX_HEADER = "WeatherApp/Support/WeatherApp-Prefix.pch";
679 | INFOPLIST_FILE = "WeatherApp/Support/WeatherApp-Info.plist";
680 | PRODUCT_NAME = "$(TARGET_NAME)";
681 | WRAPPER_EXTENSION = app;
682 | };
683 | name = Coverage;
684 | };
685 | 3F9D7F0E181A15C400CD7CCF /* Coverage */ = {
686 | isa = XCBuildConfiguration;
687 | baseConfigurationReference = A0A7F38C05814B88B8169146 /* Pods-WeatherAppTests.xcconfig */;
688 | buildSettings = {
689 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
690 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/WeatherApp.app/WeatherApp";
691 | CLANG_ENABLE_MODULES = NO;
692 | FRAMEWORK_SEARCH_PATHS = (
693 | "$(SDKROOT)/Developer/Library/Frameworks",
694 | "$(inherited)",
695 | "$(DEVELOPER_FRAMEWORKS_DIR)",
696 | );
697 | GCC_GENERATE_TEST_COVERAGE_FILES = YES;
698 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
699 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
700 | GCC_PREFIX_HEADER = "WeatherApp/Support/WeatherApp-Prefix.pch";
701 | GCC_PREPROCESSOR_DEFINITIONS = (
702 | "DEBUG=1",
703 | "$(inherited)",
704 | );
705 | INFOPLIST_FILE = "WeatherAppTests/Support/WeatherAppTests-Info.plist";
706 | PRODUCT_NAME = "$(TARGET_NAME)";
707 | TEST_HOST = "$(BUNDLE_LOADER)";
708 | WRAPPER_EXTENSION = xctest;
709 | };
710 | name = Coverage;
711 | };
712 | /* End XCBuildConfiguration section */
713 |
714 | /* Begin XCConfigurationList section */
715 | 3F81F1FA18187071008C9510 /* Build configuration list for PBXProject "WeatherApp" */ = {
716 | isa = XCConfigurationList;
717 | buildConfigurations = (
718 | 3F81F22F18187072008C9510 /* Debug */,
719 | 3F9D7F0C181A15C400CD7CCF /* Coverage */,
720 | 3F81F23018187072008C9510 /* Release */,
721 | );
722 | defaultConfigurationIsVisible = 0;
723 | defaultConfigurationName = Release;
724 | };
725 | 3F81F23118187072008C9510 /* Build configuration list for PBXNativeTarget "WeatherApp" */ = {
726 | isa = XCConfigurationList;
727 | buildConfigurations = (
728 | 3F81F23218187072008C9510 /* Debug */,
729 | 3F9D7F0D181A15C400CD7CCF /* Coverage */,
730 | 3F81F23318187072008C9510 /* Release */,
731 | );
732 | defaultConfigurationIsVisible = 0;
733 | defaultConfigurationName = Release;
734 | };
735 | 3F81F23418187072008C9510 /* Build configuration list for PBXNativeTarget "WeatherAppTests" */ = {
736 | isa = XCConfigurationList;
737 | buildConfigurations = (
738 | 3F81F23518187072008C9510 /* Debug */,
739 | 3F9D7F0E181A15C400CD7CCF /* Coverage */,
740 | 3F81F23618187072008C9510 /* Release */,
741 | );
742 | defaultConfigurationIsVisible = 0;
743 | defaultConfigurationName = Release;
744 | };
745 | /* End XCConfigurationList section */
746 | };
747 | rootObject = 3F81F1F718187071008C9510 /* Project object */;
748 | }
749 |
--------------------------------------------------------------------------------