From 7f0a2b1291eb587e73108447148162717899140a Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Thu, 22 Jan 2026 12:21:08 +0530 Subject: [PATCH] Changes by Abhinav Kumar --- .../UserInterfaceState.xcuserstate | Bin 0 -> 22495 bytes .../xcdebugger/Breakpoints_v2.xcbkptlist | 6 ++ .../xcschemes/xcschememanagement.plist | 14 ++++ Assignment/api/ApiService.swift | 43 +++++++++--- Assignment/ui/ContentView.swift | 65 ++++++++++++++---- Assignment/vm/ContentViewModel.swift | 31 ++++++--- 6 files changed, 123 insertions(+), 36 deletions(-) create mode 100644 Assignment.xcodeproj/project.xcworkspace/xcuserdata/abhinavkumar.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 Assignment.xcodeproj/xcuserdata/abhinavkumar.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist create mode 100644 Assignment.xcodeproj/xcuserdata/abhinavkumar.xcuserdatad/xcschemes/xcschememanagement.plist diff --git a/Assignment.xcodeproj/project.xcworkspace/xcuserdata/abhinavkumar.xcuserdatad/UserInterfaceState.xcuserstate b/Assignment.xcodeproj/project.xcworkspace/xcuserdata/abhinavkumar.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..73ff6274030d46bfbf403a86e322c9df8a968b65 GIT binary patch literal 22495 zcmd^ncVJUh+xR(mm5#I-P0}j{_y=R&xZ6i>iEoBoz8z9g&B}suY^j-%l;y}fL zi?#>|B2xhY1x3M)fFO#Bh>C!SIB~rGo_lZ7LDBbnKi}`KpEV>o=eg%R=RD7O_Q}+? zW{2C8o_-WzL?9ATh(-+ZLjhys=h$6thqHBDyv@}(!vWvw<2}x{vGLBSGwqEYw;bWc zE3MLyn!2I(4trC%7wA1S2>Fk$^VmFg7H^c{(Xpcm0T^eQ@t-b8PqPtZ^3XY>oYf__E6q2JLT=wIkhj4*`* zaS)c^V64Jw9ENo`2FKz!JQ!bxGjSHq#yL0_7vd6Jj*Yki55>dqa9o3@;hA_gcH#MW z0bYo2!MEbY_+I<~ei%Q3SKuz(jd$Uf@N4)0eit9Y@8OT|r}%Sx9$&!U;EVWM{4@TA zkP@MUmWU(bi3B2z$RhHHGQvQV6Gozf7)lHynur;MlV~Gm6CK2SVgYe8aSL%PaSw4n z@euJa@d(jLbP+EQFA}d3uMr1`cZoy9d&CLiL*gUi3*u|y8{#7IE%7t)3n?W-Ni7*c zMv@8SU~&kVMy8V)WD!|LR*)vrOb#b&$a-=H=^$s4v&d$$g={6AWE<%w=aTcu1>{1K zB^Q&okxR&>Q%0(Ss-lKc6R3&QBx*8crKV7gR1-CwT0kwN7Ew1)H&QIcQ8!UH zQ@2nns7I;CsK==%s3)nFR43I%byFLuP1I&;3$>MciP}r;qh6+7p$J;@Eb(*?J{XqRl{YL$Z`je*VL9{;|K+9j?=bH& zCzubJkC@Mx)66;M{OI`B_U7gzNP>b<2$CWNQjMvpOPyu!aF2&~uS95wZ)~=?-CanA z~*YeBaFB=sJ|U0clYLibPQ;8tG6BibZiK9wnf`Xb4NQ z4C}`ZV*S|wHjoWsC2TMo!b(}$29$`BP%=tEdXx&Ubd-TIQ5MQZIjo!=!hXh1V~4V5 z;PnOj75giPgE%~doyw+LrD3)9MnKivv3kE7M*0ih~LOAmoI! zYq( z7yHXyE?et#K%~`UvQKTFZfTw7v`S@wQ-!nDW1r^{mk7gzifH%8)+$XOW^;QgoGoq5 zc8|SjB(E9V9vDqrp3&w81;DQt7Ex<&bGm@AA$9g1g%11!&L_ zO@{@RGNTbsqe@hTs*wd0%q#}3T7tz97m(wJq)NIV%B#eaSJq7Y3)DX0+Zci zb2RtvK;L<03JcQy^99-39BJikjyk)GU)8_`IfaQLt#3GjhsR-S z?%!&a4*9!hM)Eea_=Hue|654=bep@~UFU2EF<${#Sf$tf9lWsJ6~H>p&gs6DK7t-a zxt(YQtM5dQv1x32Um|o0L`bztN6gbZ#HHx%g67gUd37$x#Hr1;)>%CbY3AJgf{Ka? zqdvbPt5TnBGUezCva&1m*%`*n+`RPia&vmFZ}wH_DU`c%C7UjiV=a0bbWcs)@OBTc zeoPL~0?t;OtD_sOMeESSi8Xb-ZN4#{LC^9lsA1zf(MC3tUx8>@Y(dYX+-|fLZ9~to zS!_0&(~Y*H9cU+;%jU6#Jn^D{Y}S#(EOqr(v&m9FqSjhx9%innw~VN<4mFRv7Jzx| z$nu)H9su2d5gIIY7UM8;EeyktA*!U8(1?}nVDaU!X71SLiJInk`{V*)rC^ma|5-f;F*bwsIXh4;B#r_bs}FzJvGg!3wGZ z+lT)f#!g}slkZ z`#?22S^=MHFR8rUGsEeEi3WD_vZT`4+~jDT4))dnJm$J=ZNp$>5MN+BV~j?0VuG#q zau%l1h*g-ue*BBIu-6N(!BV-@e~_0;a0r$oRTq|G89SV<>B0&e%8p=1^ENfupUobd zNS9&#H^nN|jf8!g?sB%bHr3kQV5PdR0`X0t!4dof;aJO#WJh)3NF2r1vUP0zWS__w zW^bMDnK6OiAxmqMeV*MdBeTQ>#e0tle-zf?Azl2T_YItg^+?r?lW;OlVH?;n?AUIc ziqmj9JB}UCPUKe*HDJTsjwbt5n=5s$qscSFDjhrkpzW$<)qzHW7 z55L(@vc|92SY0b%lS0m!P<_I5|p)DDl`JwhrS^~rBr{#avKwPN4q*z|bq z40eKW;&Bl!ZU9?vw8hkgi_pZwxD=i*8~FSl>-{&by;OC0{ReMMTCdtTft?U%ND`(* z(m>xHnQ#@q`DR?nPGN0bI0{F?*4MK2qo)B=xvwIDzwfaTc>I5I;_(FW#A7R-f^A5T z8{xz^c~AUwwvC+)pI!Wk7pXd(XVV$1-N&Z0fK9=768^wb68{EEkq;d=p2M%ggWK5| ztfLFh#q-#iY|DR-M;Bq1pWp`I(OEo?a=@d_*WghZQ|`m*Hhd?~eJMQuv157u>&8og z|L$g6f&ZLr{b9Te2zTE=!Y#S@%!{!#=DT*@_!6^u>)f$Gc)T1y9~=n!>vxyj7K!2J zm1|eLylDU3<9Qm3Bu3I$A106DCqx=Q4m5W2H0A*H<^c!H2fqq${Evvc$xGaA_&NMM z5O)U<+KH~1>Wzas5K=5Mcf&}7UgQXH5ItTHaJo>NW zH`oq#eiwcVzs)XSZ{UghZ)MEi$|&Cihw(9T&U2|mZ<1BaFlRffd*^q5oc7kvE zGx#fheOmk_JBq!LC(qYF9(DkEWYnfOL#+1zhx(qrgufSwdl`s(%U?h3gTF#h{|Rvk zS|l#vPXrKwNKZ(BxQo5Sy`5dg-UpxW?;$QB1L6{L_BJnZ3FS3Ut``|1f`|s>iAW-f zy@OrSMd*ka_D=Th{~BqD!9* zn0BD**Uq=1#vbnUiviLS*#Md|5VS!(EMsM>p<~Cpf7sHEXXzf&68S_CzrF$x8~1?N zAc{e3+|d(_~3^VWB()4j`Nas5;2*u z0%>hP+6TR)UBPyHCQ@BO8XFU5$*i) z-GqmIlzps=m_y8EA7@wo*TgchkhqbbU=i%^6a4M5$h+1{-e=h7*yrK%_FnSt;>o+4ecDG}a6ncOdx?Ggi(SXA7hZ#%?2wGU(YL85iBnh0 za^f@MG|2L;>^4D`6JG+&z8XlgM>}5o`2ok!fL+R0Kg;;8vz90CIRHIB5cHDu-4mVi zio%B;`ladU+ygS6ykTDQM))wfM2Lc!;05!Jzee5%e}(*y1T*Os%%ndVK!RW<8=#_Cxj~ z_EYv0ADmE&zOlW@;<2|_n)v9|GzUbVC%A=x58nztq_jy0s`k1fkYi|b*{9iEE_;*c z#qfdMF}wpb$=%rOXawId!w2C3XcF&R&w-e=&@b5Y z;%@Su{&U_(K7dr6 zIa@yo;ypDGsIT+5U?0c#Mi<4ku7Gn1`6`l6EeKI4@(FS!&neK>Np`YddVL~t6(0oR z0euRDTuVL!hk<;WTt}{F&$3^$=eo%aMG>e!uaYNqRD$ThT z0H1Avv_(_@G>oxBd{B69@U@=n56@5FThnvocAgT?vllwa9qc!3df$B@cf&c{L%ztT zQ#_6qdtCW}doLf@l%`@YiX_=f?gNh(LfG~$a&M1+PrgFF$`87qz0^s*#(vj-P#E$J z@=fxsaWJS`*n0M|Ra%%^kZI1!$;sE3=j3PTv&|K``uyyQ485@+qq3kf8!~_8=~hSE z3@3z494+mT({Z}2Hdjk-wus6*_q)MpoD@FnHGlhe-}o>rf8)Bgz5o_6vE1{FVHT{GI%RoyuVv6x&b^ zGaUBg@E{KRb2xy*fo%PFhuhrjn9j%UAe-p1xu)A8=g4P3AROg^^k$1ys_NTpaf3u@ zbU|KmK*tydB=ziWUSx_-)R~4h*jx@9pE&L}_AqCokXN-z`}hkLq3j4Ev{ zj%GN6%D(srSs^hrA96JxN(Z)-HqG%g3G35e1^qwbtkD8%LkRtDb{M+r+FM#610Wv7I{S1!v+5o#B()nd z^2)O^v!&+**-OQv{oPaoHJBR0VORzr9o-{csU#{D4lk8VrBHeft2nIYa9B5r7Bph(ek)oRS?n{#!{W(P?Vtn z?NJGl*JDx9x|rDDxcG#@L#~6iI>&rF%r&jD**4wHr;6+lkax6!$cM6$YMWayfdGzI z-#reT>~<;ZHwX$nAeF94g>+}Ly{-eQ5n4uD_&JOaHa!xYoT5)n1A|PQw|Cqg1`fZv zr_Taaau(l0v4i-PW#nkPGqbYAijwZ!ynLamM9C_Iv==`izlffPAtY8)S5eQmSK4iS zc?TF(Q1vn0-3KJh2Qu0++6`Hdp02~H;8H_p85FIA@^c%_+O7%{U%=8;S;fj&IX|lw zM)!@(7g#u5y@n<%xTm+iqs`viBSli?aLtI3qpnrC!0&ud;;w6CZBO|EJW)UT>g-m1 zLoLu+$aH}w7T3yey08>@V%&I;*H`au*M!<@v;y>`$zI;*np6v4r+|$s@560PA|G~5 zg~zYajs`)oQ8Yhv}FSI}gZ4{J-IH7ECAruQPMSQaKar7j58f}6+;ww-bcmT3< zZ=$!+QAn1Zh3wbAus`HhAO`|*zi1o-g@DCS^fw90{1$>9ycsXScSD)qeL|t%3j7#; z0(NBmLjIdi3=N~|Q0{7woHf)4N>7cVY6bbJ<8Ta!P!7j(IBqpnPpYW~Y7BUwAXwws zJsgIK27@EIhnK3ce=q)c?)tYtfD-@>`TLQ0pgK%J|W`iF{ zIjEV`EUKAmp;{>?1$rit!$}-Y=5Pv!K@p{LIE};UYf%p6raZiDM9t#=<*lL&4rhvn zQ8NF=@1qh{FIGTV+?7y7R|lq!y>)U=Yo!Zhn^juMH~6aK>TOedy)0{Pr7^d>JTG6L z0Tpuk?DF(VeZJY0uQ!{r$_pxU3ktx{gt7w|03_yc$8^wN_9j6wxhq}HmYO=q{(+R> z)ANEe#dC_NHATE&Hlxio#^x3Z(VFdJ9G)3P zcIpml33Vs6l)8($o5NWg&gO6qhjTfc$KiYq7jU?c!$nU6WtLI*QTJ01P|K+Y;oHMJ zZHhTOmc!#XJc+}TIs7r-_9;&xDWG2mY3mx>9LID~_w!9XUwNMzishhbw1+R#bJ#__ zBtB_rb+-vpxEHMFNkM&yU}BC|fXT?4$k$T|g?Q8Lu3iLsh6mL$+}`4JbyPv}c!m$q z$ET~Pr+MS=DQY#fhFZ(v5)PMg80-tfDry}xKSOQcaJkSvjKd@NCF=UsnDpS6+Cx!T zpRZi6rx%2K6=aXw#13zsy=R~rr-!dV<11*yk3G-%@Z3i2l8#-JE>iOWheC(hb<7E z22<@_>JarFq_!aU&mSwH{s-zFU3NE^x`HFZVepQwA0Z=(ud?GZa)97`#HJj~C#a!n z;^p5s2beDcwv+lskld5O9D-!^NT(av&e@t(eqb3={`lnW&-w++Lsj7sy4d)^NhxWE z2l)p`>f3Cd8RMWF)IP5oDlEGL1B3eb_(05&E^t%qb9KCE0W_t4hcVcClZAK;Uq&I! z4^!Ox`|bNr7E2L~s*rUC0+dqbZGLtQG^#Z)c3AD47AXOJT3_Gsbm)tWhQ6peDCm=7 z=#24ozP}JU<78cd{BQ}r*262Hbx0ySa$Rk^LrOwlvaj#M3(%*RL0@WvSV#xG>Av10 zPjqHNf0k?&Jev*N>s?fDp<-8VL04XWoKKa;?bV4Ny9$fq44zZMmpH@iVxwS2#6w|l zF&YZWqY=$OEod&>I#>+13|4^o@GO)e?S$I}d%=Ks2OWW$oDb1SFd)uB3C;I#%is!_ z5q@y1AOwd(`9v(-B}jyHW;!mwrErJ90=Eao;Bj~|Zp3rJuvi2)2krrr;!$z-9>rhm z#m4)FSBYBxDD^(bYxBIeW>JcdX!r1y82nlVH+K%tgI;d$rjAj^`PkY25Z)@CYOVKF zRWxMeLa?oO4mj7DxxHWeOv!70cb<1TYeStiE29@q;GsGYq^;9?ZBMK3G@YPMqTDX( zL+T?A59e@A7f35nfEoDHpU@**1gYW^$AS<_1v{`^D3=DSg-?2 zpszs&ou@8vc%&eMz(yL~6BMStrM~Mu_?J06imivk+Z17bhMJ@7LREN^ zvEAcw!ZW?Mqw9VA8I&QUCLI834m?^Daak|hsEL`Fql%U$qU|6NhKUQZ!R5*xRY zrQxB3!B=|>2~j#2|GK!&L@3UuMU&!cT&$uNF}qw&7Yw3@62>Siu%rkF90tO*M@k7R zUX_-fq5HQ`R;i|U7$J@<@ThmsuI>|dRp|Q*&teh?hgR?|ZUV9JEP5Bc2g2Ze5C&j1!XA9@D}{eRmVT`s^fpo$HhNU zzw|QW6%IpbmQM@)At;*q5z;MJv*w_Gq-Q`0Y`k~v%9UOIL0tiX*ASzU;DKUvkSsp6 zcxe6ThH(?^t+VHg%fUXkCZzxJ(9PcE(TFB`mPbR>v7u*qGz1qxkB<4jS>BHS+vUX> z+6=;yed`RQgF*b!L9~R!<2gK`3vH*RESRqodFeL<@;5Hf(PA2?$E*~jUr(u{#RRdp zn0}Cup0Q5{Td^;U;NH+m!M*7rlMf9I9S>=2I-J(h5p*OSMMu**I);v=<2Y>P@DvW) zI1I*dBZr$fZ0GPa4o~OsjCFJZN}-3)*U^b|5}iz^(0Ur2AqNZnvpC$$;T8^a9Dbg| zM>u?x!|w~Gr_ZkJwSem&vgHx#2uFg+VwENt?GP7+n$Q*qzB$~^R>8u9{D;fyk%*6q zcAw7s1?m|ceB_583qpK+z)>u39N!l@u1)OnO)gwygmTEfrW*KYzHYls7*CK8u+a)I z54|^h1S4ED@pay&-diyJ$8%2}Zky+5fv6A!FMv28nSCa_FgmZdkI+RlpC^sLv*{85 zfbwMCaO3wwthI#0+ow&CNF1Ii*aLJKZGa0eJr8uz1C zcZ&TT1N%L&ymopHJr~tY@D=F7#iMb2=FaW0wZL{P;P65g{qu`Ce|ZOrUO+E|^8xzV zM`a)9bkU3W?YUML=q&n1nx(n%ZNTT^wsQDJ4&Pvvmj5rWX~9hta0lC5PVd>cg}!xy zXf+57+D$K}Z-dj(r-}E3Akq)F*Z>JLR0DU2l)r;s0+%s*96~Q6_Oe0$L*7N-$uIMN z3@`HSU1)zN4Y7-xda@k!GWubN)zSCS_k%26PCrOL1VKj*-y(?MTRFU#!?&$QdU^%@ zDEvJRaVAiRw{!Ro;r}IKkSP-47wyeWd=z6O#F%RBv%v=D@9PPvmkIod8tQ0m0%v+s zuXN1FN``YFeivgvUU3y-af{!*;um(zH-c3Jmy$kZvX*`ZG9L8P^g4PyhwtR@QV!qM zO>dx|#ZerFrEY_Y63}jP7|~tsu7enh33ee9% zww?wJa}PWC1ih1ff!;;$ruP72y-2@A@1-ZuFVnBk`{`%t*XRRqp<*eBBi|Y6Ct&!D z0%+1{AjtT57;jm7v()|R3~Pro<>_;=1tRdRf@|XCkgMSpcHz2BKjdMb`#rOqU})UnLD7+&4*PQlLdu6OgfXnWODc! z4nsus*={DA$>GEQ8#%m*FLi?;i&(A2Gm9t~Q~5h4eec}-FE6M3+lw)W889E2Yn0gy ztb9GpK$#tLjWWA|^|ZeAn;5b1nSsJ*4sQWR1xjOcuTdHcwsW7zTc4zbG``YQVb01b z$kJD28_V_C>E#9bf=UzQE=*Y&`PoqNQ=S1=h~U}~GlCfjG9NUrxQBgoL>DuP*Rt2_ z^Wj>@K+*Lork)wiG%#a0yq&`kY~IP?7gjOjnDL~VnaJT?9Nx=kxL#+|$MQxoZ-b8$ z>|*}Yc+p%I9SlBU(_^RinEk%l8krgVY)y=vnZ|%uw41{)?28~nej?5?%V70h|?FT(rxdhg5EHA1`xV)p$yg~#DueQzcko>%n) z5k}_~nGaVtvk($mjE8Aw<}h=ac}xd0p8?ZwABSJ&@GBhN&*4`&{2GT3aQNU_v09l= zWr=^h%!EHak0t(r8s|$WdTj9~ptsuz_vax&GR!g61umHQBLqIbhtEk}6PnND38rd) z_@uvrA89ewL!TJS@>(e=e+4`oswCDh>L>r~TE? zBVA5D#?Nc}{!jcR?mfsA$)E7%0}QjupWH|T{n#Y+ZnNFq~ zetc#Pu*!O71GACY3_m^d8grOA${b@p@Jsc}^E3Jl^&92a;Aiz~_G|TP^K!1n@=1il}5JSZ_JHz+SCKd3OMB4}LDl%STN`9X_< zZVb99=$4?xLH7r(2zo5&iJ+B1Yl5B*S|79_Xk*Z!pzkE1l1NFEL?=m5>9T zp`=)1mefloNZKVgNtQ|Omn@e&BzZ*gsN`|Ulagm8n3L+%c_H{`yM2SOeUc{pT6$fF@^LpFqL4A~s= ze8`TF7eaQ2oCx_{>Msq3yC!m}TB?z1rIFGUX}+{bYLc3zRnn2t(bB0>hjf;-Me3B! zk(`)?5OOR>;u_{vXin;WT#}OWoKkx%6^mmA^TH~<)oaJ`^o*~ zfpUpFL@twu%OmB{@)&uNJWHM<&yyF(i{vHpGWjt1D0!WHw0yjLqI|M^ihPlLrF?^Y zi+r2>dHF8+9{Ef1eey%{Bl1)7ujF6L&&$7&{~-TK{)_xq`R@u^Ay*_QhA7e%C5n-X zTEzs#B*jdHQ!!iNR?Jh(S1eTAptwu1La|Y?S+P~|oMO9Tr(&04kK!f8KE*4F4-_9N zPAWc8oKl=toKbwKIIB3PxS+VG_*wC*;&;WrLj6Leq4LnsP*rGHXn1HuXkut;XnJU7 zXkKVRXi;cM=#~D>X{3GE$kMOjG773zb#MVagiiNabkd809$S1m#TST;)>b-O78F_bDGx zKB#3ygC|^|`P`<8wQ+ZT*TzNwIk@Af48|Amk z@08ywe^magqE!B>K$S!#SB0unsxVcNYOKnxYF4$X+Egx8hiZXpk?KYjr&^+VNVQJ& zjOsbn%c}QP$5m%kU#rfmzENFP{h<0u^^4k19iUdImFiga5OtzDS)H!VRA;Mm)n;|A zdb--7o~3S4JJqw*ZgsnQuDV0LKz*|`YrW4>O<Z9so>d)0@)#ua~)ZeMUSO2K~IV?0RFU%BX2^$(VJghdX zKCB^ZY}oX$nPGFoZU|$;ZVJ04?9Q;e!tM!M7IuHwV_{E+?GAe}>_FJju&=_t4!a!o zg9d9D%^*#HCPX9CC^SkDv;?$#{R+^<=#c|x;B^R#BYW`kyN5&l*9*Wnk#FNI$Y|3OP?rP>7T5N)D1S*zEkX*0B0 z+8k}3wm@5>9jUF=)@vKIW3}V86Sb4IQ?ygHP1+W1o7ScEXcuW0Yj4*s(Js~At-V)! zpY{Q5r*^e=t#+Muqjs})tM)nVVeO9*M1)_2e?(w}G(sK`8lj3vh`25yC!#o_G{O*J zjIcxujTjy=B4Sj;_=xEd^CK2U+!}FT#8VM#A~r>AiFheuf5dAM2P58&csJs`h$9iF zBfgEe9PvZM&k59BHa%JSRk^3T#M1B@|F7kZjH<3R@kx>Cr(kM+-cvM7Gag;f#HmWYFKB^(A zF=|@WjHp>rEm6)WE^0~Col$p1Js9<9)Xu2aq7FnIjCv#Lc+`hcC!;=%`Yh`6sPCeF zkEWvsMF&I&MQfrXqNAc?qT`|yqEn+YqO+oNqK(m(=%LZWqenzHM30RgA3ZU8a`cVS z+oMlKe-nKv`f~J-(LYCD(NQ{yPN|F1>2!&@bX|_ls59%Tbl2;K>1uT2bW?TBx>ntK z-8S88y0>-5bf4?a=`QFl>MrSi)?Ly4ru#$pXH0QSN6gxo%`q>;?2dUg=AD>BF(+b9 z$DE1zE|!TM6dMp56dN2Xja9^|Vl}Z5vC*;FvAMAYu|=^Zv4+@+*veQ-?6BAov7=%q z#=2vd#y%ChBlg|cGqD%r$T)4BKCUc|jawY|SX_79vvIrP_QxHFdn4}cxI=L#;!ehW z8h1MGi@2}iQM@)jH{KENjGq%fFP@9PJ^s%4yW^L|-ygp+eog$k_zm%!;Ga3{1U+>mfH4ne<%Ji%AEP4kaB< z`Z*aT<76^fnjDiHpFAWvDOsPKo}87On_Q55ee$s65y`d5^~nv%X)ib)u+~_PDyp7Hm5pMU8(J7f@x1`>dx+HaJYIo}P)I+J~Q-4XllKN}v?`i6^ z__QHuNoo4D^t8;hqO`GT6VfJUJdv>`<50%&Oq6NQbY;%XoS(TU^QO#OGjGpalDQ@G zc;=bRZ!#}sexLbM=9MfWi_RL96_^#ACC!S=(q+YE4bDo;O3ryaXI;*QoJ~1fbDq!H zk+Uynf6js2g}Ha+F3r6s_rBcaxew*8%_g?U`w&3TLSZqM72 zcP#JIyi<9n^Umk}k}t`Z<}315`I`KQ{OJ7H{Dl1L@{{uo`4#!*{ObIn`8D~Y^6T@* z_Nd?8h+ zE*xA~S~#k(rLe8gT{x$(qi|v24TYNucNV@}_-f(7!Z!=wDLhp8apBp*i-q46eqZ=g zQ9zNRD6=T9sG+E_Xj+k@sJX~lG`r}ABChD>qQynaik>LyEb1;=U9`FA#iBQgjuss+ z`mpF^ab|H*@u=d4;^yM^Vz&6^;>E>p6dx)6wD`;7%OzBaq$Izjq+~|P>=IAO+>(xx z#U=NbJXrEb$zvr?mUNXoRkEdITgmp47fSY&>@9h@WPiz-l1nB3DwUVUm*$q{mzqn5 zm5wN_EgfAtvD8{RwX~^pTIqt)`%BlAZY_PjbZ6E1O@oxNKF~ znzD6e8_G77Z7qAgY-ic7vOQ(T%1)G>Ec>+VblDeWUzJ@dyHfUh*`Ef&KpXrFp$4@< zV~8*$8d3~thD<}5!DuiUstompF@|x5iH7M0k72H1zG0!^KEsoSZHDcJ7Yut0dkwD` zUNgLIc+2pv;jrN=!`FuMhKq(vhVKnO8h$ohG5l`$S2-$=FRv({QhrnU>hibBFBwCO zsm7tkdgB=5c;iH)-PmeuGrEi(;~e8W6-1vg=W8*pFx5mrH zAB2soV5|>452w>4@o=>4fQ|=?l|Y(|OZ3 zrc0*Frr*r8S!%w{oMSFEmzz!ID)aT`I`e4r81p#u1oI^G0`tS>UFM_a&&-$2Kbn6r z|7QMIC9b3@{VD@0wUv>T(Umckag_;`Ln;$1lPmR=6Dsel+)?>emAuMW<*Dkf+Elf* z>iMdjRlBQRtU6rvQPruc&#S(yI$QN))$dh*Ruk29bx?IkwX8a{T2&oiol$M79$r1Z z+E(3EJ-vEn_3Ua-^_=RC>IK!eR4=Q(uloM#<<$>Wuc}^Cy{>vg^``1A)w`i+?% + + diff --git a/Assignment.xcodeproj/xcuserdata/abhinavkumar.xcuserdatad/xcschemes/xcschememanagement.plist b/Assignment.xcodeproj/xcuserdata/abhinavkumar.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..ec91b33 --- /dev/null +++ b/Assignment.xcodeproj/xcuserdata/abhinavkumar.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + Assignment.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/Assignment/api/ApiService.swift b/Assignment/api/ApiService.swift index 46662a1..95acd44 100644 --- a/Assignment/api/ApiService.swift +++ b/Assignment/api/ApiService.swift @@ -7,27 +7,48 @@ import Foundation -class ApiService : NSObject { +class ApiService { private let baseUrl = "" private let sourcesURL = URL(string: "https://api.restful-api.dev/objects")! - func fetchDeviceDetails(completion : @escaping ([DeviceData]) -> ()){ - URLSession.shared.dataTask(with: sourcesURL) { (data, urlResponse, error) in + func fetchDeviceDetails(completion : @escaping (Result<[DeviceData], Error>) -> Void) { + URLSession.shared.dataTask(with: sourcesURL) { data, response, error in if let error = error { print("Network error: \(error.localizedDescription)") - completion([]) // Return an empty array on network failure + DispatchQueue.main.async{ + completion(.failure(error)) + } + // Return an empty array on network failure return } - if let data = data { - let jsonDecoder = JSONDecoder() - let empData = try! jsonDecoder.decode([DeviceData].self, from: data) - if (empData.isEmpty) { - completion([]) - // Error + + guard let data = data else { + DispatchQueue.main.async{ + completion(.success([])) + + } + return + } + do { + let devices = try JSONDecoder().decode([DeviceData].self, from: data) + DispatchQueue.main.async{ + completion(.success(devices)) + } + } catch{ + print("decode error") + DispatchQueue.main.async{ + completion(.failure(error)) + // let jsonDecoder = JSONDecoder() + // let empData = try! jsonDecoder.decode([DeviceData].self, from: data) + // if (empData.isEmpty) { + // completion([]) + // // Error + // }} } } - }.resume() + } .resume() + } } diff --git a/Assignment/ui/ContentView.swift b/Assignment/ui/ContentView.swift index 253924a..bab4840 100644 --- a/Assignment/ui/ContentView.swift +++ b/Assignment/ui/ContentView.swift @@ -8,11 +8,15 @@ import UIKit -class ContentViewController: UIViewController { +class ContentViewController: UIViewController{ + + private let viewModel = ContentViewModel() private var devices: [DeviceData] = [] + private var filteredDevices: [DeviceData] = [] private var tableView: UITableView! + private let searchController = UISearchController(searchResultsController: nil) private var activityIndicator: UIActivityIndicatorView! override func viewDidLoad() { @@ -38,46 +42,79 @@ class ContentViewController: UIViewController { activityIndicator.center = self.view.center activityIndicator.hidesWhenStopped = true view.addSubview(activityIndicator) - + //api data fetch + setupSearchBar() fetchData() navigationItem.title = "Computers" view.backgroundColor = .white + } func fetchData() { activityIndicator.startAnimating() - - DispatchQueue.main.asyncAfter(deadline: .now() + 1) { - if let data = self.viewModel.data { - self.devices = data - self.tableView.reloadData() - } - self.activityIndicator.stopAnimating() +// +// DispatchQueue.main.asyncAfter(deadline: .now() + 1) { +// if let data = self.viewModel.data { +// self.devices = data +// print(data) +// +// self.tableView.reloadData() +// } + viewModel.fetchDevices{ [weak self] devices in + guard let self = self else { return} + self.devices = devices + self.tableView.reloadData() + self.activityIndicator.stopAnimating() self.tableView.isHidden = false } } + private var isFiltering:Bool{ + searchController.isActive && + !(searchController.searchBar.text?.isEmpty ?? true) + } + private func setupSearchBar(){ + searchController.searchResultsUpdater = self + searchController.obscuresBackgroundDuringPresentation = false + searchController.searchBar.placeholder = "Search Devices" + navigationItem.searchController = searchController + navigationItem.hidesSearchBarWhenScrolling = false + definesPresentationContext = true + } } extension ContentViewController: UITableViewDataSource, UITableViewDelegate { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return devices.count + return isFiltering ? filteredDevices.count: devices.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "DeviceCell", for: indexPath) - let device = devices[indexPath.row] + let device = isFiltering ? filteredDevices[indexPath.row] + :devices[indexPath.row] cell.textLabel?.text = device.name return cell } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - let selectedDevice = devices[indexPath.row] - _ = DetailViewController(device: selectedDevice) + let device = isFiltering ? filteredDevices[indexPath.row] + :devices[indexPath.row] + let detailVC = DetailViewController(device: device) + navigationController?.pushViewController(detailVC, animated: true) + } } - +extension ContentViewController: UISearchResultsUpdating{ +func updateSearchResults(for searchController: UISearchController) { + let searchText = searchController.searchBar.text ?? "" + filteredDevices = devices.filter{ + $0.name.lowercased().contains(searchText.lowercased()) + } + tableView.reloadData() + + } +} diff --git a/Assignment/vm/ContentViewModel.swift b/Assignment/vm/ContentViewModel.swift index b4d8326..7f3d562 100644 --- a/Assignment/vm/ContentViewModel.swift +++ b/Assignment/vm/ContentViewModel.swift @@ -8,19 +8,28 @@ import Foundation -class ContentViewModel : ObservableObject { +class ContentViewModel { private let apiService = ApiService() - @Published var navigateDetail: DeviceData? = nil - var data: [DeviceData]? = [] +// @Published var navigateDetail: DeviceData? = nil +// var data: [DeviceData]? = [] + private var devices: [DeviceData] = [] - func fetchAPI() { - apiService.fetchDeviceDetails(completion: { item in - self.data = item - }) - } - func navigateToDetail(navigateDetail: DeviceData) { - self.navigateDetail = navigateDetail + func fetchDevices(completion:@escaping ([DeviceData]) -> Void){ + apiService.fetchDeviceDetails{ result in + switch result { + case.success(let devices): + completion(devices) + case .failure(let error): + print("error",error.localizedDescription) + completion([]) + } + } + } } -} + +// func navigateToDetail(navigateDetail: DeviceData) { +// self.navigateDetail = navigateDetail +// } +