├── 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 | ![screenshot-1](https://raw.githubusercontent.com/Ruenzuo/res/master/ci-screenshot-1.png)  23 | ![screenshot-2](https://raw.githubusercontent.com/Ruenzuo/res/master/ci-screenshot-2.png) 24 | ![screenshot-3](https://raw.githubusercontent.com/Ruenzuo/res/master/ci-screenshot-3.png) 25 | 26 | __Jenkins Dashboard:__ 27 | 28 | The main objective here is to have a quality metrics dashboard on Jenkins: 29 | 30 | ![test_results](https://raw.githubusercontent.com/Ruenzuo/res/master/test_results.png) 31 | 32 | ![code_coverage](https://raw.githubusercontent.com/Ruenzuo/res/master/code_coverage.png) 33 | 34 | ![static_analysis](https://raw.githubusercontent.com/Ruenzuo/res/master/static_analysis.png) 35 | 36 | ![sloc_count](https://raw.githubusercontent.com/Ruenzuo/res/master/sloc_count.png) 37 | 38 | ![duplicate_code](https://raw.githubusercontent.com/Ruenzuo/res/master/duplicate_code.png) 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 | --------------------------------------------------------------------------------