From bb34a2635f97798d81475cf195eda17693c7d2e8 Mon Sep 17 00:00:00 2001 From: Ian Fijolek Date: Fri, 19 Jan 2018 19:46:40 -0800 Subject: [PATCH] Clean up of view, passing back scopes, add icon --- .../AppIcon.appiconset/Contents.json | 6 +- .../AppIcon.appiconset/icon-120.png | Bin 0 -> 6140 bytes .../AppIcon.appiconset/icon-180.png | Bin 0 -> 9105 bytes FitKit/Assets.xcassets/Contents.json | 6 ++ FitKit/Base.lproj/Main.storyboard | 8 +-- FitKit/FitbitClient.swift | 39 ++++++++---- FitKit/ViewController.swift | 57 ++++++++++++------ 7 files changed, 80 insertions(+), 36 deletions(-) create mode 100644 FitKit/Assets.xcassets/AppIcon.appiconset/icon-120.png create mode 100644 FitKit/Assets.xcassets/AppIcon.appiconset/icon-180.png create mode 100644 FitKit/Assets.xcassets/Contents.json diff --git a/FitKit/Assets.xcassets/AppIcon.appiconset/Contents.json b/FitKit/Assets.xcassets/AppIcon.appiconset/Contents.json index d8db8d6..c3bd516 100644 --- a/FitKit/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/FitKit/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -31,13 +31,15 @@ "scale" : "3x" }, { - "idiom" : "iphone", "size" : "60x60", + "idiom" : "iphone", + "filename" : "icon-120.png", "scale" : "2x" }, { - "idiom" : "iphone", "size" : "60x60", + "idiom" : "iphone", + "filename" : "icon-180.png", "scale" : "3x" }, { diff --git a/FitKit/Assets.xcassets/AppIcon.appiconset/icon-120.png b/FitKit/Assets.xcassets/AppIcon.appiconset/icon-120.png new file mode 100644 index 0000000000000000000000000000000000000000..ae6b3b9995612c16b2410568ca090af5e2415d47 GIT binary patch literal 6140 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

-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 O0000}!JM-*bDr}( z_jC7KkwBa?Nwe@S1;BZaYpW`s*0!d#Uolw25EO&4>5HNeXb0p279KJQHc$>6MpJ@f z9k5D4v2C$Rg(w#(hFn?cok2;#j4^J4Ohd!&j`F#8d&Gp_r^Uwsmq2YVW_UK(a)cnmlu1^^Ly#D;( zMHBVtqKWz?pT!Q3%pT&v|0vj5)J6Sc!~{L+bB;~Ysg=>-6%3S6{w zoGM9}$}wV+?t|h(kIWug07bwj!zbxp36aplU(mf&m{~w~Z*4uaAa>domn^?at#7h~ zu2sTEOw@%c%v&CrJ-7hfESrCgn5bI=6iw7klhEh9pmV}u=GKUO4<6it<-n+=<5YFb z68iF2v5+4X$X4Orgu zDII8&MJeLs_Ta&-sBROWaONE<7**wHoyDsjnLVTdvX6#%EviviG*OQRX5odiJ*0`0 zR+TbZ-y*2WXX1HA|tJ~*<9#)gv?DO#_ zZ*4rJBYf~jfRRsY(V>SvuM2z;O1!1TXVw}{$%FnKp>r!}6@a!mP>=(mh!FiqCG0DQZAW3_At(=}FR3-qDhv9whtpd_ z$9!m;E5r@>ptK5(R6$t?_Ljk#{cx}%BS~%f)Ag4}YzWyx*dA@*%(gJROU+GfBR8`t zs0hKjgRtfRtlke>j=-kF5VnOB$^{Q;$c8K*^lk@dw1q((;j9kO_Y}x&;+>3W*nSi? z9D-$g;MG-7 z8iec?YTI5FmL2D|A#ll=&@NXba-zSZJOt|wz>6zj;ZE>d^706sge_HTXrBj{o(Z=P zflevb{Qt8S{3W+Z|13KilteP`#2mE~%e7qg<0*O~ebdCD+#na)Ap>R&OmR8yVVfa5n9&} z`NAIX-E-lLCWL(sREVLoyA(bM4q8nBO#mdj|iDb>o67qB@fYS=a$m&)Az1oQp z7z>V6!5`}rQ zh%r|cK2EUD7hD;hoLc=H@rvXJpfCr9b%AU8z=*D~J%ICfz6xx6RbJP@+|vHj+`JKzWJ#dpmo1)*&% z(&CSoH!hW{!?5A75c%B=@?IicYY`iQw)k|%jgZ}^E2gLps58 zS2w+Tsn6M3Zglt7;$^sF7!*Wtmz9U$;n^^GgGl>mC=0^a;qcva zqN_NiL3nXF%-AFq{GpxU+J10t-^L%bCl|uMK6Qt|=n6uAr62t4{OC7o zR|(uRP4vW;5(YVb_{~M|r5@4Gdwf2;w8ELFVHW3a`Nj2m4p>Y3b=WY5}6+FEdemFyg1knEf-sZ+O@gw`7F83}g`cRpw1Au+c0m%B$tt9=kVw}Qtmlbh^>rxwC9 ziy=SjxSH|57s2)Yo&RRVUiire;{SEqHFwB|-(Ld5Iy;~Dw-xZzqLzCITg0OnvEj0w z(aAh{J$&sANuse_<{PqVq&TS2=pMgGtwzy#)~) ze)SPd-AGy?7=c>`!2@4#@Mwf28_vni_Jj9e*4Fe(1iG>}{PF^Gyr8ri#!i9FhcjH| z_{nHz*VmH^#pp^mWckD={?tPA@m2vdnKkg>?DQlvNW#{W4%b|eE%!Rrs#D6Au`CD= zUno&H6Mi=bURfDiq{^+N{@V)C{bR+%B`{@udXgFBUcIssemB>Ae0Ud;;AO!KBebo> z2prSf*#LZVZQ>$d&D9a()xng4pS%yB>}dSlH`l_S7P!y&YqAlh;LSB+D4K9pA2AYb zok4`o^-BoZgdNA=k-71cD3L~9RT$djNRm=zxTXfNiOA1_2R@KW5GO3z4Uc}3_$2+7 z6atUTft?Nwna7WS+(3p9I%31cXPOglA~xLqwh*bM1yxl9e(^<^c^mxqX889F&^jl+ z&#DN*&u5w+(qKfSc*rJWpb!CY``cg}u4`aNxbSp|D5f=`b&a0&%L~lMADb_cz?RO| zvbBVvZyI>~z5X4fQY2p0W9>os@eGL!)}lNFcTTP;P$jdV6dbLTduqaiUlKb*r!%3e z!xCID89lp8CHC)z)I*n6shPI6lOzfhdk^Nr=F+>dx&kvdt!n4*yTObkkDF7?+8-~)6(6r z^O$=UKWsY(dqA$iBXrYur5(rQUf0Ljr^>FKtb~iC5V|@ddCn%x-rjNv>rR9|J4%K1 z(9Rw~<3UweT0NNX^&!HMv{E^4=x>(XGd8))Fp6kVE1Zz!lPYQLakR+>@$}7RN&K|| zX(XYwhC!W#2=%dGm(<0%h0j_;msU{NNW`vJyX4(~OjbL#AB9D`8Xn8>3rCW%rimvs zFv3A6ePaz|xhVN4qGRh!)T^CG(0GvTmz)(7in=6c*Go8|EhSH%3G;S{3(GZxZbW~s zMNf~=aUo#A90xsmaHr%XW+j|nxdGY6>tpr)YovU=frPf*Md;cwXr}QNYY&QlwEhg2E(MZi+MLGbCo`cGUHjy9Rl zrZ9Ifl6dtkp>k6xanYDAC@0A*h_ykCuF;20?!^^*#5H%QQu@)=N1yhI*=d^7%qAU? z?RV~fmuM9=;e;_=|PPlh!^sTI) zLzD7(JA}-(hT^^Oh0_`mx>LRkzK;i0VJYRW?IN}20NgWGBvmFOFCZLfAfbaUS2=3+ z)A~UaMbl>CH3iq7b)3uqqq@P5&x`)h^>#=H-FQfPwfPmSFnBt00`X6>wYBucuGf^lb&Q;vG4m~*595}xHDFyq= z8U|8)O8VZ#t4G#-@M>}0S?S-fu`zTnZ1~=hs^ruaCY;dK5p$&NjJApYp05u!OaAVy zW#*wtKg|o6dvL6+m*Q9%GAC5Uo9y2{SCZOIJH`$DlUFqA2AtNq;d@6blM`%9IH9Y; zW<7dvr^J1St|7=hBc0Fr#xQ9hXd-kcgW9mZUboQ7u$j>Dlw*Z$83pHLN8B--j7FdK z4c|LdnVkHdglD9NBht;jesN?XI|pek(m7hv{rRUshdgPvU8_n&G@RAJtR~fO1)E4h zSBIlFer%r$U+4}CcV(XXk>zWcMgXv%?e|7(7-?vJQxSqar45ZZQ(}5;0mH!&P0L3% zp_@{~hC7Efe%|md&W3C<30EDKtv?x=grNwG>0>^&Hc5m|tHL5(vjhBibZqCJk|&Az zOLxoP<_FAPiXF$q7hJa-O92}lCOD1g3U958Ed<`XeQXBt>22h-GDVYEJGX){y~HQ4 zluW&ywN-YiM5#S~Wp2(GwqfPI^8v^(4Oz!xGs<4crzG>iz!{#sS0pp5f z_~$c=n_m>HYdTVy+Ey&sDfiN!gpnL+)#UZmLb#@{`PlE~N>`@(_;T;#2H{8tLNDHB z&NZlxH1vJ|oLUh3xQZ)!n@MFV=en>5w8>3gI6N2;GT%PrL^9W+RTkVm623XC`DwB? z;@L6boejxrIFwWgo#lg@2f@{SM0z&&K4tr4s84+q?~|@f=XR5AyDmEBi_ZJuHs_8` zEB2bZ>iaDj@YBzb`_a1{tUu_i9@tv^-9?7)T)bN{`mQ^x;juf0O1J;|Hb=;oIW>2W z6i-V$YKbDYT$Gy!!qg3B_iL=k513uGww8O5tcF8LCG?fO;l31vb|0B5+w}XJWsF#T zeD7Sz(=hs<4+>eL5&G+c&F;H(Q4E`^uuOk4VZjcVw#iKB%X`7|%T63Bq> zoYl2J1VeE%^!YY9QV>xe^L8Y+*f^;w#}`gd{^Bp%1>25_Kl8(F<{^)LPk~E&np0R8 z?{YRyd=@NsNcQj5F8YqsCRcW*23`*>WZqjQlG!J zXIfXM7Q1Od=wm5pUGT3@k4K!ehQBU1pZBdIvk|v23ZWJ9fS9)4?ya5wE~4RD2Y1!1 zE#e}~@tc2Zj$a1e*EO#Cog$Jum8c36zh6zmHw^v3ORJp)L|PDf^7`bqFPOg*rfod# zX3F+Ul+uJg?HjiEjno8BqPeLQu*qN;M(d`O<8z*%_2-51ca{=Aw`pS3m30isstEky z($tr*tvL{16fb0p2gMZT-(4muUNVtX9eU9&8266cphOsjb*0wY1MupqhAys<4IgZg zalDOTPibtExHcY!qIwa=j(I{%)8DttGaE#!_rtC_e%>QhVvyF){_EUI+9u53DFfh5 z$yC*0_+V?()B;;eN`R^NU|lTP4F@XXyEi#LxOK4k*o(`PAKFj4mt*xl=c3c*Kq+%m zb%2>$B}&+sL9@3kHbQSc((t_w`R3?n*oKSFFjwfjv*E-WYHN6Zi+S|j_s@mBAHenf z9muf)o>|ni;DlYHzPx@zlyhwO^+)kf_dTbZxHaqJUx;}=_m(4~y42R{ z#rg{PWVgBcHJL?T5t83;Ybjk@wGS?S1wJc@kIWiTH%#i$vx`$xD4mf#HFuQA_TJb` zymc|^bNkDoDg=Rg5x-%bB@xT=9VhdJJ^beDaw{hQ=gxEI05_rMawZXr~iZj^6E|ry&ukEGXU> zr|S$x;KQwr|BjMscyf{ZS|DgG1r??!ZpfAgHdP(9Ql@f*BXE9C_~{oK8e%2Y@a=b+ z7iz7KT)#xze(^=~@hwNB#cwRZidwHq{p5b1CG~O#D@5uI?`)n3@Z$2ux7!c4!k5m7 zzGK|~zDPQ^jae?tk#M;QPcIS`C)vhe$|ZEbCsOKgrMRW)zWTHm9Ws$KSbUcB*)T;8 z;dsk}m=?(r_nHYqJByUon2TV85qNKNayMb_XR(vR=g`)mme64vy0(%eeG^d+$Dluc zcFa7B1Y}2>w9I|wS;2_hvzrE*U4(yGl=u=W*BON~x4_mT=3_rSzj>XBObh;zL+-%x zy-kqNs$o_5=Z0Y?A>3U>=Hnl4kF6=8Pr*wooDE8+VuO}Mr&jRu^PN??>6=pjn^I2b zh=vC~FdyrX2lt$vxN;y%!Q1N^lHuQL;>-9lk?NE@@kg5yk!%g`Zj8+!@>%f74pD{b zV%Z*eYLRmgsihFMVSJHu1EHVKYMBupzOGk4^iWElASFiBfeLd0Az#OR9= z8)j^l?<=i_S60H`i<_QFrjaOH{V^87h}12bLf!9gshOS|BN+oeaiQ%m7pec|S_z=o z+Fec7tHN+&e;Du3M&glAB=fN)^nFz{v1Qr?=J??+V?+lv;riDlEi_T?p0F+C_FM5e z_#;|!Qhsw$L$_2YVh(kG<)5M(H&yqo{jEOBeMX?xQiu5Jb>`!%_lw7>vV~^qwAgZ0 zgrvoQ6aH{%Vmp@lE$6nbu^NV^a;$)5{++A$$4BP6zt!);uvbn19{!T^@ki!1-T^iv z32iC4>A(EYe7t8n`2QCqr^|c1aLSD~p?)G#CZIYZMxawWwBOE=a=4bf?iQ5+0U!K# z9lX22eEhPW@ZEFW(?hv3Bts+{LupV_Ff$3ZmJC2sW?-$o@XysPZ@jknrWWwQz9w74ccw|ciyPnyLi;SqAp71lXCL&W zYa~6fC8xP6|Wq*Lcg>kbBw`m1@PEq&V31=TMBbNb63#ERdPfrcw#=hz21Dhbq>6Ao%DUVcI76{oLmFzdI=iU8}o2G8aDGZ$923U-Y`ptx~U3 zTQ!|JP55|wa@#hfV%QAyvuyNeIC60RjIp6 zrS~IXNxp>%Q`V=i{#@G({_od0XFM+0DQziAgYLZ!*Am)N5}Eta4CmC%cDW)=`gKU1 zpSreCVMo0Y7LUyn_hUNpS!?M-_vbG=_w4xe0Q~rU2`;($p>46`q*!5VNh|&H7}4#F zHv}R6tSzbQFw`zr8mgRzD)cKtChh&)Pp$hl!K&~frEvXB$ z#f3DYtFvYP#C&*lrMZ)6!lfD~rgf&Hsu8lK-SLk`IeTW7>=t2BQk@>xm=+GETJ_`m z?}-!NNw@v+XvzIhD*2Vs3dz8;RQi!w*ZlT-M#2N5oyoj%pR9RVFnz8utvsgXK^Xsz zMB zy?cx3-X5t>By)Zi{QJh}U8SaNgx`EDfueNVebO?f6|v#xGoyDLYMTph-z;57gAtF+ z!H6_ceD@ZqP?qOHu2G60A{xd`cJ9^Z5!%HDQQKTexEatP`ni9cC!UaCB>iduHKJDB zH~@YyD*Cw_4@m~&!HUdlv5@hEt|fJjA8s8aJ@cZWc%Sr@SiLWO+Q%@r166k*C7rmpjiEti{70!{jtE7{~O6hc9fL^!Lw@5pgF2P8vc7 zBXC-4cqj&G_U=ZQuvl7o1$^$ijvj4b>~OfIZ}fAQ?SbFTfpv#c+jQI`bgJ}CExy)U z2Eo0Js2~R`#Jx9tvwO%8j7Y=CAB~pjRnhS0h0@8WDxBFW#z{@+y4&#JPNITzZWaC9 zsT<)BpGYg-mNFQ%hE~~NQ<%6^D$jk2lM*K_q3e1=hISVBUSkunKG-7NQd5<^ z=eI=XzoJ+4^U6cwPhYg_1gHM;W1uJGFR zjgxuSR=D9`;>9@G$?Qq!dU|TWCyDRBza%<2sw2`l=#ftnzqQ+1W;tHlFM9McV#}8mX)N&Qv&3l^IPT zJwm5Iul6aprXT!dbaY91tXf8EytOv|2xCP^ychS6hWsd>5w@iT*W2sl0q~@57H>$nFD#Ef?`2GH zc;Q-68)6ypTpRIw?fU3su8zpe@|)lE$m~hz1gjx^+QH*jL{HBwt%jd`0CToGSAbN7 z#bff@i=$8a+EoHSoFTfpr(SpxI;*~UCv?P9J>n;I zL6-CdiPni|=P~g4pi8Uh(){6%#h~=a9RDp*MHBTgpwL^3I8_k9ui*Or(kwC_JTzN! zu-!JKJ`Hiy0*-m>(2Omv#)x}u75rejw4IF=CDriV=`u}Mdt^=oj#(OxcZ z4Zbx6)*Wp8{7r|YyXWj}scX`H65xoX6dSyCNDPI5wrBtV0}@F@K~w@Y14PG8cAgCb zlHK>sx5Z!#cw|oGZn?oiv(j6N#G*1J!*yO?T~FVxX&m{jDU!e8A;I>Hrr58b*z?;Z zSW+m_haa5>Gd9Cl&XVZhl=U(et@a!b35LZ=ziqKn*=RhQBq><27Z&Z7#SK^2Hs2-V>)|2IF=Nf$ z%C-QiBK*}`4-e@KUllI{SVNxGe0sO40N(JH#Y0+JgYf#I@2c{VPwV5Jjo}mZsS0*_ z%i|&45Qc8WyE)On8W#JBPs{iH*gdYC9 zX4Q=pY1B~?6&x4Dwv@FP|_4g@b0R8AelZ39L zh7=lod7+$2H| zpQKrJiwZ3rr^YL|3NQHLA$h1&iZLbKxU2Y1RXzN<*h3<57X=kPtNr+R70tCkws#La zBmh+krduW4wCH{nS58q}gsySxiR7NB&sA_IFxI;t9-4ud(LB3!yjlP-;u+17yH#ok z-9R0gpowTs^(&r0af5enJT!rT{|dnm0>1FBC1V4L_`Jko;Ile6+u~MW4A2+o1NgmL z=b^^X+W>3^rUiVwvS6$_mf!~@aTxNfruf_Pk>@?3t*Sg) - - + + @@ -44,7 +44,7 @@ - + diff --git a/FitKit/FitbitClient.swift b/FitKit/FitbitClient.swift index 12e75e2..7f638e9 100644 --- a/FitKit/FitbitClient.swift +++ b/FitKit/FitbitClient.swift @@ -15,6 +15,7 @@ import OAuthSwiftAlamofire class FitbitClient { private enum FitKitRequestError: Error { case unableToParseResults + case invalidAuthorization } private var oauthClient: OAuth2Swift @@ -24,6 +25,7 @@ class FitbitClient { private let authorizeUrl = "https://www.fitbit.com/oauth2/authorize" private let accessTokenUrl = "https://api.fitbit.com/oauth2/token" private var keychainWrapper: KeychainWrapper + private let keychainCredKey = "credential" init() { // Init inner client @@ -44,10 +46,11 @@ class FitbitClient { self.keychainWrapper = KeychainWrapper.standard // Attempt to load tokens - self.loadStoredTokens() + self.maybeLoadStoredCredential() } - func loadStoredTokens() { + /// Attempts to load and use stored credentials + func maybeLoadStoredCredential() { if let credential = self.readCredential() { NSLog("Using credential from Keychain") self.oauthClient.client.credential.oauthToken = credential.oauthToken @@ -55,8 +58,11 @@ class FitbitClient { } } + /// Attempts to read OAuthSwiftCredential from the keychain + /// + /// - Returns: If a valid credential is found, it will be returned func readCredential() -> OAuthSwiftCredential? { - if let credential = self.keychainWrapper.object(forKey: "credential") as? OAuthSwiftCredential { + if let credential = self.keychainWrapper.object(forKey: keychainCredKey) as? OAuthSwiftCredential { NSLog("Found credential in keychain") return credential } @@ -64,10 +70,21 @@ class FitbitClient { return nil } + /// Persists an OAuthSwiftCredential into the keychain + /// + /// - Parameter credential: credential to be persisted + /// - Returns: TRUE if persisted successfully private func storeCredential(_ credential: OAuthSwiftCredential) -> Bool { - return self.keychainWrapper.set(credential, forKey: "credential") + return self.keychainWrapper.set(credential, forKey: keychainCredKey) } - + + /// Clears credential token and expiration + func clearCredential() { + self.oauthClient.client.credential.oauthToken = "" + self.oauthClient.client.credential.oauthTokenExpiresAt = nil + self.keychainWrapper.removeObject(forKey: keychainCredKey) + } + /// Check if the current client is already authorized /// /// - Returns: True if the current client can make requests @@ -90,12 +107,13 @@ class FitbitClient { /// - viewController: the source controller to create the new Safari view /// - success: Callback to be executed on success /// - failure: Callback to be executed on failure - func authorize(viewController: UIViewController, success: @escaping () -> Void, failure: @escaping () -> Void) { + func authorize(viewController: UIViewController, callback: @escaping (String?, Error?) -> Void) { // Set webview handler self.oauthClient.authorizeURLHandler = SafariURLHandler( viewController: viewController, oauthSwift: self.oauthClient ) + // Authorize let _ = self.oauthClient.authorize( withCallbackURL: URL(string: self.callbackUrl)!, @@ -108,19 +126,18 @@ class FitbitClient { let scope = parameters["scope"] as? String else { NSLog("Invalid authorization response") - failure() + callback(nil, FitKitRequestError.invalidAuthorization) return } NSLog("Authorized scope: \(scope)") NSLog("Succesfully authenticated with credentials \(credential.oauthToken)") let _ = self.storeCredential(credential) - success() + callback(scope, nil) }, failure: { error in - // TODO: Maybe return the error - NSLog(error.localizedDescription) - failure() + NSLog("Error authorizing \(error.localizedDescription)") + callback(nil, error) } ) } diff --git a/FitKit/ViewController.swift b/FitKit/ViewController.swift index 6687dcf..c1a06bf 100644 --- a/FitKit/ViewController.swift +++ b/FitKit/ViewController.swift @@ -23,7 +23,7 @@ class ViewController: UIViewController { } override func viewDidAppear(_ animated: Bool) { - authorize() + ensureAuthorized(callback: self.updateView) } override func didReceiveMemoryWarning() { @@ -34,35 +34,53 @@ class ViewController: UIViewController { fileprivate func updateAuthorizedLabel() { if self.client.isAuthorized() { self.loggedIn.text = "Logged In" + //self.loginButton.currentTitle = "Log Out" } else { self.loggedIn.text = "Not Logged In" + //self.loginButton.currentTitle = "Log In" } } - fileprivate func authorize() { - HealthKitHelper.authorizeHealthKit() { - result, error in - // TODO: Do something with the result and error - if !self.client.isAuthorized() { - self.client.authorize(viewController: self, success: self.authorized, failure: self.unauthroized) - } else { - // TODO: this should be made significantly more clear - self.displayWeight() + /// Ensure that the client is authorized and then continue + fileprivate func ensureAuthorized(callback: @escaping (Bool) -> Void) { + // If already authroized, we can short circuit + if self.client.isAuthorized() { + callback(true) + return + } + // Authorize the client + self.client.authorize(viewController: self) { + scope, error in + if error != nil { + NSLog("Error encountered when attempting authorization: \(error.debugDescription)") + } + guard let scope = scope else { + NSLog("Did not retreive any authorized scope") + callback(false) + return + } + NSLog("Authorized for scope: \(scope). Requesting HealthKit access") + HealthKitHelper.authorizeHealthKit() { + result, error in + // TODO: Do something with the result and error + if error != nil { + callback(false) + } else { + callback(true) + } } } } - fileprivate func authorized() { - NSLog("We have Fitbit auth!!!") - updateAuthorizedLabel() - self.displayWeight() - } - - fileprivate func unauthroized() { - NSLog("No auth. Booo!!!") + fileprivate func updateView(authSuccess: Bool) { + self.updateAuthorizedLabel() + if authSuccess { + self.syncWeights() + } } - func displayWeight() { + /// Synchronizes weights from Fitbit to HealthKit + func syncWeights() { let today = Date() let lastMonth = Calendar.current.date(byAdding: .day, value: -31, to: today)! self.client.getWeight(start: lastMonth, end: today) { @@ -70,6 +88,7 @@ class ViewController: UIViewController { fbWeights, error in if let error = error { NSLog("Unable to get weights: \(error)") + self.outputText.text = "Error getting weights" return }