diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c52d52b --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# Mac OS X +.DS_Store + +# Xcode +*.pbxuser +*.perspective +*.perspectivev3 +*.mode1v3 +*.mode2v3 +*.xcuserstate diff --git a/codeChallenge.xcodeproj/project.pbxproj b/codeChallenge.xcodeproj/project.pbxproj index 06a6ec1..3adf872 100644 --- a/codeChallenge.xcodeproj/project.pbxproj +++ b/codeChallenge.xcodeproj/project.pbxproj @@ -7,8 +7,13 @@ objects = { /* Begin PBXBuildFile section */ + 0745256A266A3BA800342E43 /* RestApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07452569266A3BA800342E43 /* RestApi.swift */; }; + 074C69BE266B40E700E82DBE /* CommonMethods.m in Sources */ = {isa = PBXBuildFile; fileRef = 074C69BD266B40E700E82DBE /* CommonMethods.m */; }; + 074C69C1266B568500E82DBE /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = 074C69C0266B568500E82DBE /* .gitignore */; }; + 075989722668A309002129B1 /* PhotosModal.m in Sources */ = {isa = PBXBuildFile; fileRef = 075989712668A309002129B1 /* PhotosModal.m */; }; + 0764840C2668EDEB00022B6F /* DetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0764840B2668EDEB00022B6F /* DetailViewController.m */; }; 3E15BBE4208754FE0029597E /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3E15BBE3208754FE0029597E /* AppDelegate.m */; }; - 3E15BBE7208754FE0029597E /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3E15BBE6208754FE0029597E /* ViewController.m */; }; + 3E15BBE7208754FE0029597E /* FoodItemListVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 3E15BBE6208754FE0029597E /* FoodItemListVC.m */; }; 3E15BBEA208754FE0029597E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3E15BBE8208754FE0029597E /* Main.storyboard */; }; 3E15BBEC208754FE0029597E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3E15BBEB208754FE0029597E /* Assets.xcassets */; }; 3E15BBEF208754FE0029597E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3E15BBED208754FE0029597E /* LaunchScreen.storyboard */; }; @@ -18,11 +23,22 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 07452568266A3BA800342E43 /* codeChallenge-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "codeChallenge-Bridging-Header.h"; sourceTree = ""; }; + 07452569266A3BA800342E43 /* RestApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestApi.swift; sourceTree = ""; }; + 074C69BC266B40E700E82DBE /* CommonMethods.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CommonMethods.h; sourceTree = ""; }; + 074C69BD266B40E700E82DBE /* CommonMethods.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CommonMethods.m; sourceTree = ""; }; + 074C69C0266B568500E82DBE /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = ""; }; + 0759896B26689E22002129B1 /* Constant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Constant.h; sourceTree = ""; }; + 075989702668A309002129B1 /* PhotosModal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PhotosModal.h; sourceTree = ""; }; + 075989712668A309002129B1 /* PhotosModal.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PhotosModal.m; sourceTree = ""; }; + 076484072668C31600022B6F /* PrefixHeader.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PrefixHeader.pch; sourceTree = ""; }; + 0764840A2668EDEB00022B6F /* DetailViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DetailViewController.h; sourceTree = ""; }; + 0764840B2668EDEB00022B6F /* DetailViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DetailViewController.m; sourceTree = ""; }; 3E15BBDF208754FE0029597E /* codeChallenge.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = codeChallenge.app; sourceTree = BUILT_PRODUCTS_DIR; }; 3E15BBE2208754FE0029597E /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 3E15BBE3208754FE0029597E /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - 3E15BBE5208754FE0029597E /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; - 3E15BBE6208754FE0029597E /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + 3E15BBE5208754FE0029597E /* FoodItemListVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FoodItemListVC.h; sourceTree = ""; }; + 3E15BBE6208754FE0029597E /* FoodItemListVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FoodItemListVC.m; sourceTree = ""; }; 3E15BBE9208754FE0029597E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 3E15BBEB208754FE0029597E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 3E15BBEE208754FE0029597E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; @@ -44,9 +60,66 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 074C69C4266B73C800E82DBE /* Common */ = { + isa = PBXGroup; + children = ( + 0759896B26689E22002129B1 /* Constant.h */, + 074C69BC266B40E700E82DBE /* CommonMethods.h */, + 074C69BD266B40E700E82DBE /* CommonMethods.m */, + ); + path = Common; + sourceTree = ""; + }; + 0759896626689B3B002129B1 /* ApiManager */ = { + isa = PBXGroup; + children = ( + 07452569266A3BA800342E43 /* RestApi.swift */, + ); + path = ApiManager; + sourceTree = ""; + }; + 0759896D2668A13F002129B1 /* ViewControllers */ = { + isa = PBXGroup; + children = ( + 3E15BBE5208754FE0029597E /* FoodItemListVC.h */, + 3E15BBE6208754FE0029597E /* FoodItemListVC.m */, + 0764840A2668EDEB00022B6F /* DetailViewController.h */, + 0764840B2668EDEB00022B6F /* DetailViewController.m */, + ); + path = ViewControllers; + sourceTree = ""; + }; + 0759896E2668A170002129B1 /* Views */ = { + isa = PBXGroup; + children = ( + 3E15BBF8208774B10029597E /* CustomCell.h */, + 3E15BBF9208774B10029597E /* CustomCell.m */, + 3E15BBFB20879B4B0029597E /* CustomCell.xib */, + ); + path = Views; + sourceTree = ""; + }; + 0759896F2668A1B1002129B1 /* Modal */ = { + isa = PBXGroup; + children = ( + 075989702668A309002129B1 /* PhotosModal.h */, + 075989712668A309002129B1 /* PhotosModal.m */, + ); + path = Modal; + sourceTree = ""; + }; + 076484062668C30A00022B6F /* PCH */ = { + isa = PBXGroup; + children = ( + 076484072668C31600022B6F /* PrefixHeader.pch */, + ); + path = PCH; + sourceTree = ""; + }; 3E15BBD6208754FE0029597E = { isa = PBXGroup; children = ( + 074C69C0266B568500E82DBE /* .gitignore */, 3E15BBE1208754FE0029597E /* codeChallenge */, 3E15BBE0208754FE0029597E /* Products */, ); @@ -63,18 +136,20 @@ 3E15BBE1208754FE0029597E /* codeChallenge */ = { isa = PBXGroup; children = ( + 074C69C4266B73C800E82DBE /* Common */, + 076484062668C30A00022B6F /* PCH */, + 0759896F2668A1B1002129B1 /* Modal */, + 0759896626689B3B002129B1 /* ApiManager */, + 0759896E2668A170002129B1 /* Views */, + 0759896D2668A13F002129B1 /* ViewControllers */, 3E15BBE2208754FE0029597E /* AppDelegate.h */, 3E15BBE3208754FE0029597E /* AppDelegate.m */, - 3E15BBE5208754FE0029597E /* ViewController.h */, - 3E15BBE6208754FE0029597E /* ViewController.m */, - 3E15BBF8208774B10029597E /* CustomCell.h */, - 3E15BBF9208774B10029597E /* CustomCell.m */, - 3E15BBFB20879B4B0029597E /* CustomCell.xib */, 3E15BBE8208754FE0029597E /* Main.storyboard */, 3E15BBEB208754FE0029597E /* Assets.xcassets */, 3E15BBED208754FE0029597E /* LaunchScreen.storyboard */, 3E15BBF0208754FE0029597E /* Info.plist */, 3E15BBF1208754FE0029597E /* main.m */, + 07452568266A3BA800342E43 /* codeChallenge-Bridging-Header.h */, ); path = codeChallenge; sourceTree = ""; @@ -105,11 +180,12 @@ 3E15BBD7208754FE0029597E /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0930; + LastUpgradeCheck = 1210; ORGANIZATIONNAME = "Fernando Suárez"; TargetAttributes = { 3E15BBDE208754FE0029597E = { CreatedOnToolsVersion = 9.3; + LastSwiftMigration = 1210; }; }; }; @@ -136,6 +212,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 074C69C1266B568500E82DBE /* .gitignore in Resources */, 3E15BBEF208754FE0029597E /* LaunchScreen.storyboard in Resources */, 3E15BBEC208754FE0029597E /* Assets.xcassets in Resources */, 3E15BBFC20879B4B0029597E /* CustomCell.xib in Resources */, @@ -150,10 +227,14 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 3E15BBE7208754FE0029597E /* ViewController.m in Sources */, + 0745256A266A3BA800342E43 /* RestApi.swift in Sources */, + 074C69BE266B40E700E82DBE /* CommonMethods.m in Sources */, + 075989722668A309002129B1 /* PhotosModal.m in Sources */, + 3E15BBE7208754FE0029597E /* FoodItemListVC.m in Sources */, 3E15BBF2208754FE0029597E /* main.m in Sources */, 3E15BBFA208774B10029597E /* CustomCell.m in Sources */, 3E15BBE4208754FE0029597E /* AppDelegate.m in Sources */, + 0764840C2668EDEB00022B6F /* DetailViewController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -205,6 +286,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -214,12 +296,14 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREFIX_HEADER = "/Users/asthayadav/Documents/Orderbird-coding-task/ios-tech-challenge/codeChallenge/PCH/PrefixHeader.pch"; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", @@ -230,9 +314,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.3; + IBC_MODULE = codeChallenge; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; + PRODUCT_MODULE_NAME = codeChallenge; SDKROOT = iphoneos; }; name = Debug; @@ -263,6 +349,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -272,19 +359,24 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "/Users/asthayadav/Documents/Orderbird-coding-task/ios-tech-challenge/codeChallenge/PCH/PrefixHeader.pch"; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.3; + IBC_MODULE = codeChallenge; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_MODULE_NAME = codeChallenge; SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; VALIDATE_PRODUCT = YES; }; name = Release; @@ -293,7 +385,9 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 56ETNADXPZ; INFOPLIST_FILE = codeChallenge/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -302,6 +396,9 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.nanosuarez.codeChallenge; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "codeChallenge/codeChallenge-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -310,7 +407,9 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 56ETNADXPZ; INFOPLIST_FILE = codeChallenge/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -319,6 +418,8 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.nanosuarez.codeChallenge; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "codeChallenge/codeChallenge-Bridging-Header.h"; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; diff --git a/codeChallenge.xcodeproj/xcuserdata/asthayadav.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/codeChallenge.xcodeproj/xcuserdata/asthayadav.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..083823e --- /dev/null +++ b/codeChallenge.xcodeproj/xcuserdata/asthayadav.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/codeChallenge.xcodeproj/xcuserdata/asthayadav.xcuserdatad/xcschemes/xcschememanagement.plist b/codeChallenge.xcodeproj/xcuserdata/asthayadav.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..3023e5a --- /dev/null +++ b/codeChallenge.xcodeproj/xcuserdata/asthayadav.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + codeChallenge.xcscheme_^#shared#^_ + + orderHint + 0 + + + SuppressBuildableAutocreation + + 3E15BBDE208754FE0029597E + + primary + + + + + diff --git a/codeChallenge/ApiManager/RestApi.swift b/codeChallenge/ApiManager/RestApi.swift new file mode 100644 index 0000000..4d2259c --- /dev/null +++ b/codeChallenge/ApiManager/RestApi.swift @@ -0,0 +1,55 @@ +// +// RestApi.swift +// codeChallenge +// +// Created by Astha yadav on 04/06/21. +// Copyright © 2021 Fernando Suárez. All rights reserved. +// + +import Foundation +import UIKit + +let FLICKR_APIKEY = "2ed35a9f4fda03bc96e73dbd03602780" +let BASEURL = "https://api.flickr.com/services/rest/" +let tags = "cooking" + + + +class RestApi : NSObject { + + static var sharedInstance:RestApi { + let instance = RestApi() + return instance + } + + @objc func getFoodItemList(pageNo:Int,perPageCount:Int,sort:String){ + + let extra = "date_taken,views,description,date_upload,date_taken,owner_name,icon_server,original_format,last_update,views,media,url_t,url_l" + + let requestParams = String(format: "method=flickr.photos.search&api_key=%@&tags=%@&page=%ld&per_page=%ld&format=json&nojsoncallback=1&sort=%@&extras=%@", FLICKR_APIKEY,tags,pageNo,perPageCount,sort,extra) + + let completeUrlString = String(format: "%@?%@", BASEURL,requestParams) + + let session = URLSession.shared + guard let url = URL(string: completeUrlString) else { return } + let task = session.dataTask(with: url, completionHandler: { data, response, error in + if error == nil { + do { + if let jsonData = data { + let json = try JSONSerialization.jsonObject(with: jsonData, options: []) as! [String: Any] + + NotificationCenter.default.post(name: NSNotification.Name(rawValue: "getJSON"), object: self, userInfo: json) + + + } + } catch { + print(error.localizedDescription) + } + }else{ + print(error?.localizedDescription ?? "Error") + } + }) + task.resume() + } +} + diff --git a/codeChallenge/Base.lproj/Main.storyboard b/codeChallenge/Base.lproj/Main.storyboard index 8b08874..fd9e5c0 100644 --- a/codeChallenge/Base.lproj/Main.storyboard +++ b/codeChallenge/Base.lproj/Main.storyboard @@ -1,28 +1,82 @@ - - - - + + - + + + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + @@ -32,10 +86,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/codeChallenge/Common/CommonMethods.h b/codeChallenge/Common/CommonMethods.h new file mode 100644 index 0000000..15d18b2 --- /dev/null +++ b/codeChallenge/Common/CommonMethods.h @@ -0,0 +1,21 @@ +// +// CommonMethods.h +// codeChallenge +// +// Created by Astha yadav on 05/06/21. +// Copyright © 2021 Fernando Suárez. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface CommonMethods : NSObject + ++ (NSString *)stringByStrippingHTML:(NSString *)inputString; ++(void)activityIndicatorStart:(UIView*)view; ++(void)activityIndicatorStop:(UIView*)view; + +@end + +NS_ASSUME_NONNULL_END diff --git a/codeChallenge/Common/CommonMethods.m b/codeChallenge/Common/CommonMethods.m new file mode 100644 index 0000000..dd708ae --- /dev/null +++ b/codeChallenge/Common/CommonMethods.m @@ -0,0 +1,53 @@ +// +// CommonMethods.m +// codeChallenge +// +// Created by Astha yadav on 05/06/21. +// Copyright © 2021 Fernando Suárez. All rights reserved. +// + +#import "CommonMethods.h" + +@implementation CommonMethods + +UIActivityIndicatorView *indicator; + ++ (NSString *)stringByStrippingHTML:(NSString *)inputString +{ + NSMutableString *outString; + + if (inputString) + { + outString = [[NSMutableString alloc] initWithString:inputString]; + + if ([inputString length] > 0) + { + NSRange r; + + while ((r = [outString rangeOfString:@"<[^>]+>| " options:NSRegularExpressionSearch]).location != NSNotFound) + { + [outString deleteCharactersInRange:r]; + } + } + } + + return outString; +} + ++(void)activityIndicatorStart:(UIView*)view{ + indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; + indicator.hidesWhenStopped = YES; + indicator.frame = CGRectMake((view.frame.size.width/2)-15, (view.frame.size.height/2)-100, 30, 30); + + [view addSubview:indicator]; + [view isUserInteractionEnabled]; + [indicator startAnimating]; + view.userInteractionEnabled = NO; +} + ++(void)activityIndicatorStop:(UIView*)view{ + [indicator stopAnimating]; + view.userInteractionEnabled = YES; +} + +@end diff --git a/codeChallenge/Common/Constant.h b/codeChallenge/Common/Constant.h new file mode 100644 index 0000000..466197b --- /dev/null +++ b/codeChallenge/Common/Constant.h @@ -0,0 +1,13 @@ +// +// Constant.h +// codeChallenge +// +// Created by Astha yadav on 03/06/21. +// Copyright © 2021 Fernando Suárez. All rights reserved. +// + + + +#define FLICKR_APIKEY @"2ed35a9f4fda03bc96e73dbd03602780" +#define BASE_URL = @"https://api.flickr.com/services/rest/?" + diff --git a/codeChallenge/CustomCell.m b/codeChallenge/CustomCell.m deleted file mode 100644 index 0e73dd7..0000000 --- a/codeChallenge/CustomCell.m +++ /dev/null @@ -1,22 +0,0 @@ -// -// CustomCell.m -// codeChallenge -// -// Created by Nano Suarez on 18/04/2018. -// Copyright © 2018 Fernando Suárez. All rights reserved. -// - -#import "CustomCell.h" - -@implementation CustomCell - -- (void)awakeFromNib { - [super awakeFromNib]; - // Initialization code - - - self.imageTitleCell.translatesAutoresizingMaskIntoConstraints = YES; -} - - -@end diff --git a/codeChallenge/Info.plist b/codeChallenge/Info.plist index 16be3b6..f41381c 100644 --- a/codeChallenge/Info.plist +++ b/codeChallenge/Info.plist @@ -41,5 +41,10 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + diff --git a/codeChallenge/Modal/PhotosModal.h b/codeChallenge/Modal/PhotosModal.h new file mode 100644 index 0000000..e8a2ba7 --- /dev/null +++ b/codeChallenge/Modal/PhotosModal.h @@ -0,0 +1,34 @@ +// +// PhotosModal.h +// codeChallenge +// +// Created by Astha yadav on 03/06/21. +// Copyright © 2021 Fernando Suárez. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface PhotosModal : NSObject + +@property(strong,nonatomic) NSString * photoId; +@property(nonatomic,strong) NSString * title; +@property(nonatomic,strong) NSString * _content; +@property(nonatomic,strong) NSString * url_t; +@property(nonatomic,strong) NSString * url_l; +@property(nonatomic,strong) NSString * ownername; +@property(nonatomic,strong) NSString * dateupload; +@property(nonatomic,strong) NSString * lastupdate; +@property(nonatomic,strong) NSString * datetaken; +@property(nonatomic,strong) NSString * views; + + +- (instancetype)initWithJSONString:(NSDictionary *)dict; + +- (nullable NSURL*)imageLogoUrl; +- (nullable NSURL*)imageLargeUrl; + +@end + +NS_ASSUME_NONNULL_END diff --git a/codeChallenge/Modal/PhotosModal.m b/codeChallenge/Modal/PhotosModal.m new file mode 100644 index 0000000..da94f72 --- /dev/null +++ b/codeChallenge/Modal/PhotosModal.m @@ -0,0 +1,40 @@ +// +// PhotosModal.m +// codeChallenge +// +// Created by Astha yadav on 03/06/21. +// Copyright © 2021 Fernando Suárez. All rights reserved. +// + +#import "PhotosModal.h" + +@implementation PhotosModal + +- (instancetype)initWithJSONString:(NSDictionary *)dict; +{ + self = [super init]; + if (self) { + self.photoId = [dict valueForKey:@"id"]; + self.title = [dict valueForKey:@"title"]; + self.url_t = [dict valueForKey:@"url_t"]; + self.url_l = [dict valueForKey:@"url_l"]; + self.ownername = [dict valueForKey:@"ownername"]; + self.dateupload = [dict valueForKey:@"dateupload"]; + self.lastupdate = [dict valueForKey:@"lastupdate"]; + self.datetaken = [dict valueForKey:@"datetaken"]; + self._content = [[dict valueForKey:@"description"] valueForKey:@"_content"]; + self.views = [dict valueForKey:@"views"]; + } + return self; +} + +- (nullable NSURL*)imageLogoUrl { + return [NSURL URLWithString:self.url_t]; +} + +- (nullable NSURL*)imageLargeUrl { + return [NSURL URLWithString:self.url_l]; +} + +@end + diff --git a/codeChallenge/PCH/PrefixHeader.pch b/codeChallenge/PCH/PrefixHeader.pch new file mode 100644 index 0000000..2de40f4 --- /dev/null +++ b/codeChallenge/PCH/PrefixHeader.pch @@ -0,0 +1,23 @@ +// +// PrefixHeader.pch +// codeChallenge +// +// Created by Astha yadav on 03/06/21. +// Copyright © 2021 Fernando Suárez. All rights reserved. +// + +#ifndef PrefixHeader_pch +#define PrefixHeader_pch + +#import "AppDelegate.h" +#import "Constant.h" +#import "PhotosModal.h" +#import "CustomCell.h" +#import "FoodItemListVC.h" +#import "DetailViewController.h" +#import "codeChallenge-Swift.h" +#import "CommonMethods.h" + + +#endif /* PrefixHeader_pch */ + diff --git a/codeChallenge/ViewController.m b/codeChallenge/ViewController.m deleted file mode 100644 index cf24236..0000000 --- a/codeChallenge/ViewController.m +++ /dev/null @@ -1,122 +0,0 @@ -// -// ViewController.m -// codeChallenge -// -// Created by Nano Suarez on 18/04/2018. -// Copyright © 2018 Fernando Suárez. All rights reserved. -// - -#import "ViewController.h" -#import "CustomCell.h" - -NSString *const FlickrAPIKey = @"2ed35a9f4fda03bc96e73dbd03602780"; - - -@interface ViewController () -@property (nonatomic, readwrite) NSArray *photos; -@property (nonatomic) NSInteger imagePageOffset; -@property (nonatomic, copy) void (^ reloadBlock)(void); - -@end - -@implementation ViewController - -- (void)viewDidLoad { - self.imagePageOffset = 1; - [self loadFlickrPhotos]; - - UINib *cellNib = [UINib nibWithNibName:@"CustomCell" bundle:nil]; - [self.tableView registerNib:cellNib forCellReuseIdentifier:@"CustomCell"]; -} - -- (void)viewWillAppear:(BOOL)animated { - -} - - - -//TableViewDatasource - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return self.photos.count; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - - static NSString *identifier = @"CustomCell"; - - CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier]; - - if (cell == nil) { - cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]; - } - - - cell.imageTitleCell.text = [[self.photos objectAtIndex:indexPath.row] objectForKey:@"title"]; - cell.imageSubtitleCell.text = [[[self.photos objectAtIndex:indexPath.row] objectForKey:@"description"] objectForKey:@"_content"];; - - - - NSString *urlString = [NSString stringWithFormat:@"https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=%@&tags=%@&per_page=%ld&format=json&nojsoncallback=1&extras=url_t", FlickrAPIKey, @"cooking",(long)self.imagePageOffset]; - - NSURL *url = [NSURL URLWithString:urlString]; - NSURLRequest *request = [NSURLRequest requestWithURL:url]; - NSURLResponse *response = nil; - NSError *error = nil; - NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; - - if (!error) { - - NSDictionary *photos = [[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error] objectForKey:@"photos"]; - NSArray *photo = [photos objectForKey:@"photo"]; - NSString *urlPhotoString = [[photo objectAtIndex:self.imagePageOffset-1] objectForKey:@"url_t"]; - NSURL *urlPhoto = [NSURL URLWithString:urlPhotoString]; - - NSData *imageData = [[NSData alloc] initWithContentsOfURL:urlPhoto]; - UIImage *image = [[UIImage alloc] initWithData:imageData]; - - cell.imageCell.image = image; - self.imagePageOffset += 1; - - self.reloadBlock = ^{ - [self reload]; - }; - - - - dispatch_async(dispatch_get_main_queue(), self.reloadBlock); - } - - -// typedef void(^reloadBlock)() = ^() { -// [self reload]; -// }; - - return cell; -} - -- (void)reload { - [self.tableView reloadData]; -} - -- (void)loadFlickrPhotos { - NSString *urlString = [NSString stringWithFormat:@"https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=%@&tags=%@&per_page=15&format=json&nojsoncallback=1&extras=date_taken,description,tags", FlickrAPIKey, @"cooking"]; - - NSURL *URL = [NSURL URLWithString:urlString]; - NSURLRequest *request = [NSURLRequest requestWithURL:URL]; - NSURLResponse *response = nil; - NSError *error = nil; - - NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; - - if (!error) { - NSDictionary *photosDictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error]; - self.photos = [[photosDictionary objectForKey:@"photos"] objectForKey:@"photo"]; - - dispatch_async(dispatch_get_main_queue(), ^{ - [self.tableView reloadData]; - }); - } -} - -@end diff --git a/codeChallenge/ViewControllers/DetailViewController.h b/codeChallenge/ViewControllers/DetailViewController.h new file mode 100644 index 0000000..48c8d43 --- /dev/null +++ b/codeChallenge/ViewControllers/DetailViewController.h @@ -0,0 +1,25 @@ +// +// DetailViewController.h +// codeChallenge +// +// Created by Astha yadav on 03/06/21. +// Copyright © 2021 Fernando Suárez. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface DetailViewController : UIViewController + +@property (weak, nonatomic) IBOutlet UIImageView *imageDetail; +@property (weak, nonatomic) IBOutlet UILabel *labelNoOfViews; +@property (weak, nonatomic) IBOutlet UILabel *labelOwnarName; +@property (weak, nonatomic) IBOutlet UILabel *labelDescription; +@property (weak, nonatomic) IBOutlet UILabel *labelTitle; + +@property(strong,nonatomic)PhotosModal* photoDetail; + +@end + +NS_ASSUME_NONNULL_END diff --git a/codeChallenge/ViewControllers/DetailViewController.m b/codeChallenge/ViewControllers/DetailViewController.m new file mode 100644 index 0000000..c816006 --- /dev/null +++ b/codeChallenge/ViewControllers/DetailViewController.m @@ -0,0 +1,46 @@ +// +// DetailViewController.m +// codeChallenge +// +// Created by Astha yadav on 03/06/21. +// Copyright © 2021 Fernando Suárez. All rights reserved. +// + +#import "DetailViewController.h" + +@interface DetailViewController () + +@end + +@implementation DetailViewController +@synthesize labelTitle,labelOwnarName,labelDescription,labelNoOfViews; +@synthesize photoDetail; + +- (void)viewDidLoad { + [super viewDidLoad]; + + [self setDetailInfo]; +} + + +#pragma mark:- setDetailInfo +-(void)setDetailInfo{ + labelNoOfViews.text = [NSString stringWithFormat:@"Views : %@",photoDetail.views]; + labelOwnarName.text = [NSString stringWithFormat:@"Owner name : %@",photoDetail.ownername]; + labelTitle.text = photoDetail.title; + NSString*strContent = [CommonMethods stringByStrippingHTML:photoDetail._content]; + labelDescription.text = strContent; + + dispatch_async(dispatch_get_global_queue(0,0), ^{ + NSData * data = [[NSData alloc] initWithContentsOfURL: self->photoDetail.imageLargeUrl]; + if ( data == nil ) + return; + dispatch_async(dispatch_get_main_queue(), ^{ + self.imageDetail.image = [UIImage imageWithData: data]; + }); + + }); +} + + +@end diff --git a/codeChallenge/ViewController.h b/codeChallenge/ViewControllers/FoodItemListVC.h similarity index 50% rename from codeChallenge/ViewController.h rename to codeChallenge/ViewControllers/FoodItemListVC.h index 9940f82..c9d6e0b 100644 --- a/codeChallenge/ViewController.h +++ b/codeChallenge/ViewControllers/FoodItemListVC.h @@ -8,7 +8,9 @@ @import UIKit; -@interface ViewController : UITableViewController +@interface FoodItemListVC : UITableViewController +@property (weak, nonatomic) IBOutlet UISegmentedControl *segmantControl; +@property (weak, nonatomic) IBOutlet UISwitch *switchLatestOldest; @end diff --git a/codeChallenge/ViewControllers/FoodItemListVC.m b/codeChallenge/ViewControllers/FoodItemListVC.m new file mode 100644 index 0000000..7881a89 --- /dev/null +++ b/codeChallenge/ViewControllers/FoodItemListVC.m @@ -0,0 +1,168 @@ +// +// FoodItemListVC.m +// codeChallenge +// +// Created by Nano Suarez on 18/04/2018. +// Copyright © 2018 Fernando Suárez. All rights reserved. +// + +#import "FoodItemListVC.h" + +@interface FoodItemListVC (){ + NSMutableArray *photosArray; + NSInteger pageNo,totalPage; + BOOL isFirstLoad; + NSString *sortKey; + RestApi *restapi; + NSString *sortOrder; + +} + +@property (nonatomic, readwrite) NSArray *photos; +@property (nonatomic) NSInteger imagePageOffset; +@property (nonatomic, copy) void (^ reloadBlock)(void); +@property (nonatomic, weak) id delegate; + +@end + +@implementation FoodItemListVC + +- (void)viewDidLoad { + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getJSON:) name:@"getJSON" object:nil]; + + restapi = [[RestApi alloc] init]; + photosArray = [NSMutableArray new]; + + [self uiSetUp]; + [self initialData]; + + [CommonMethods activityIndicatorStart:self.view]; + [self loadFlikerPhotos:pageNo sort:sortKey]; + +} + +#pragma mark:- initialData +-(void)initialData{ + + self.imagePageOffset = 20; + pageNo = 1; + sortKey = @"date-posted-desc"; + isFirstLoad = YES; + sortOrder = @"desc"; +} + +#pragma mark:- uiSetUp +-(void)uiSetUp{ + + [_segmantControl addTarget:self action:@selector(segmentControlClick:) forControlEvents: UIControlEventValueChanged]; + + UINib *cellNib = [UINib nibWithNibName:@"CustomCell" bundle:nil]; + [self.tableView registerNib:cellNib forCellReuseIdentifier:@"CustomCell"]; + +} + +#pragma mark:- loadFlikerPhotos +-(void)loadFlikerPhotos:(NSInteger)pageNo sort:(NSString*)sortKey{ + if (pageNo == 1){ + if ([photosArray count] > 0){ + [photosArray removeAllObjects]; + } + } + [restapi getFoodItemListWithPageNo:pageNo perPageCount:self.imagePageOffset sort:sortKey]; +} + +#pragma mark:- getJSON +-(void)getJSON:(NSNotification*)notification { + NSDictionary *dict = [notification userInfo]; + + NSDictionary * dictPhotos = [dict valueForKey:@"photos"]; + NSArray *arrPhoto = [dictPhotos valueForKey:@"photo"]; + totalPage = [[dictPhotos valueForKey:@"pages"] integerValue]; + pageNo++; + isFirstLoad = NO; + for (int i = 0; i < [arrPhoto count]; i++) { + NSDictionary *json = [arrPhoto objectAtIndex: i]; + PhotosModal* p = [[PhotosModal alloc] initWithJSONString:json]; + [photosArray addObject:p]; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + [CommonMethods activityIndicatorStop:self.view]; + [self.tableView reloadData]; + }); + +} + +#pragma mark:- switchClick +- (IBAction)switchClick:(id)sender { + if ([_switchLatestOldest isOn]){ + sortOrder = @"desc"; + }else{ + sortOrder = @"asc"; + } +} + +#pragma mark:-segmentControlClick +- (void)segmentControlClick:(UISegmentedControl *)segment +{ + pageNo = 1; + switch (segment.selectedSegmentIndex) { + case 0: + sortKey = @"date-posted"; + break; + case 1: + sortKey = @"date-taken"; + break; + case 2: + sortKey = @"interestingness"; + break; + default: + break; + } + sortKey = [NSString stringWithFormat:@"%@-%@",sortKey,sortOrder]; + + [CommonMethods activityIndicatorStart:self.view]; + [self loadFlikerPhotos:pageNo sort:sortKey]; + +} + +#pragma mark:- UITableViewDatasource +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + return photosArray.count; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + + static NSString *identifier = @"CustomCell"; + + CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier]; + if (cell == nil) { + cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]; + } + + PhotosModal *objPhoto = [photosArray objectAtIndex:indexPath.row]; + [cell setDisplay:objPhoto]; + + return cell; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ + PhotosModal *objPhoto = [photosArray objectAtIndex:indexPath.row]; + DetailViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:@"DetailViewController"]; + vc.photoDetail = objPhoto; + [self.navigationController pushViewController:vc animated:YES]; +} + +- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { + if(indexPath.row == photosArray.count-2) { + if(pageNo <= totalPage && !isFirstLoad) { + [self loadFlikerPhotos:pageNo sort:sortKey]; + } + } + +} + + + +@end diff --git a/codeChallenge/CustomCell.h b/codeChallenge/Views/CustomCell.h similarity index 79% rename from codeChallenge/CustomCell.h rename to codeChallenge/Views/CustomCell.h index ead38ec..8db463e 100644 --- a/codeChallenge/CustomCell.h +++ b/codeChallenge/Views/CustomCell.h @@ -9,8 +9,14 @@ @import UIKit; @interface CustomCell : UITableViewCell +NS_ASSUME_NONNULL_BEGIN + @property (weak, nonatomic) IBOutlet UIImageView *imageCell; @property (weak, nonatomic) IBOutlet UILabel *imageTitleCell; @property (weak, nonatomic) IBOutlet UILabel *imageSubtitleCell; +- (void)setDisplay:(nullable PhotosModal *)display; + +NS_ASSUME_NONNULL_END + @end diff --git a/codeChallenge/Views/CustomCell.m b/codeChallenge/Views/CustomCell.m new file mode 100644 index 0000000..52ea660 --- /dev/null +++ b/codeChallenge/Views/CustomCell.m @@ -0,0 +1,38 @@ +// +// CustomCell.m +// codeChallenge +// +// Created by Nano Suarez on 18/04/2018. +// Copyright © 2018 Fernando Suárez. All rights reserved. +// + +#import "CustomCell.h" + +@implementation CustomCell + +- (void)awakeFromNib { + [super awakeFromNib]; + +} + +- (void)setDisplay:(nullable PhotosModal *)display { + + [self.imageTitleCell setText:display.title]; + NSString*strContent = [CommonMethods stringByStrippingHTML:display._content]; + [self.imageSubtitleCell setText:strContent]; + + dispatch_async(dispatch_get_global_queue(0,0), ^{ + NSData * data = [[NSData alloc] initWithContentsOfURL: display.imageLogoUrl]; + if ( data == nil ) + return; + dispatch_async(dispatch_get_main_queue(), ^{ + self.imageCell.image = [UIImage imageWithData: data]; + }); + + }); + + // TODO load image + [self.imageCell setBackgroundColor:UIColor.blueColor]; +} + +@end diff --git a/codeChallenge/CustomCell.xib b/codeChallenge/Views/CustomCell.xib similarity index 71% rename from codeChallenge/CustomCell.xib rename to codeChallenge/Views/CustomCell.xib index e2c4c85..78db682 100644 --- a/codeChallenge/CustomCell.xib +++ b/codeChallenge/Views/CustomCell.xib @@ -1,11 +1,9 @@ - - - - + + - + @@ -13,54 +11,54 @@ - - + + - + - + - - + - + + + - + + - + + diff --git a/codeChallenge/codeChallenge-Bridging-Header.h b/codeChallenge/codeChallenge-Bridging-Header.h new file mode 100644 index 0000000..1b2cb5d --- /dev/null +++ b/codeChallenge/codeChallenge-Bridging-Header.h @@ -0,0 +1,4 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// +