Clean up of view, passing back scopes, add icon

This commit is contained in:
IamTheFij 2018-01-19 19:46:40 -08:00
parent 4b3e0898c7
commit bb34a2635f
7 changed files with 80 additions and 36 deletions

View File

@ -31,13 +31,15 @@
"scale" : "3x" "scale" : "3x"
}, },
{ {
"idiom" : "iphone",
"size" : "60x60", "size" : "60x60",
"idiom" : "iphone",
"filename" : "icon-120.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"idiom" : "iphone",
"size" : "60x60", "size" : "60x60",
"idiom" : "iphone",
"filename" : "icon-180.png",
"scale" : "3x" "scale" : "3x"
}, },
{ {

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -27,14 +27,14 @@
</navigationBar> </navigationBar>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Logged In" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="CoH-cd-flw"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Logged In" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="CoH-cd-flw">
<rect key="frame" x="38" y="99" width="298" height="45"/> <rect key="frame" x="38" y="99" width="298" height="45"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="37"/> <fontDescription key="fontDescription" type="system" pointSize="37"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" fixedFrame="YES" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="IdY-jU-MBP"> <textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" fixedFrame="YES" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="IdY-jU-MBP">
<rect key="frame" x="16" y="152" width="343" height="495"/> <rect key="frame" x="16" y="152" width="343" height="305"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/> <fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/> <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
@ -44,7 +44,7 @@
<accessibility key="accessibilityConfiguration"> <accessibility key="accessibilityConfiguration">
<accessibilityTraits key="traits" notEnabled="YES"/> <accessibilityTraits key="traits" notEnabled="YES"/>
</accessibility> </accessibility>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/> <viewLayoutGuide key="safeArea" id="OGN-ry-fYp"/>
</view> </view>
<connections> <connections>
<outlet property="loggedIn" destination="CoH-cd-flw" id="SUh-wA-KNF"/> <outlet property="loggedIn" destination="CoH-cd-flw" id="SUh-wA-KNF"/>

View File

@ -15,6 +15,7 @@ import OAuthSwiftAlamofire
class FitbitClient { class FitbitClient {
private enum FitKitRequestError: Error { private enum FitKitRequestError: Error {
case unableToParseResults case unableToParseResults
case invalidAuthorization
} }
private var oauthClient: OAuth2Swift private var oauthClient: OAuth2Swift
@ -24,6 +25,7 @@ class FitbitClient {
private let authorizeUrl = "https://www.fitbit.com/oauth2/authorize" private let authorizeUrl = "https://www.fitbit.com/oauth2/authorize"
private let accessTokenUrl = "https://api.fitbit.com/oauth2/token" private let accessTokenUrl = "https://api.fitbit.com/oauth2/token"
private var keychainWrapper: KeychainWrapper private var keychainWrapper: KeychainWrapper
private let keychainCredKey = "credential"
init() { init() {
// Init inner client // Init inner client
@ -44,10 +46,11 @@ class FitbitClient {
self.keychainWrapper = KeychainWrapper.standard self.keychainWrapper = KeychainWrapper.standard
// Attempt to load tokens // Attempt to load tokens
self.loadStoredTokens() self.maybeLoadStoredCredential()
} }
func loadStoredTokens() { /// Attempts to load and use stored credentials
func maybeLoadStoredCredential() {
if let credential = self.readCredential() { if let credential = self.readCredential() {
NSLog("Using credential from Keychain") NSLog("Using credential from Keychain")
self.oauthClient.client.credential.oauthToken = credential.oauthToken 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? { 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") NSLog("Found credential in keychain")
return credential return credential
} }
@ -64,8 +70,19 @@ class FitbitClient {
return nil 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 { 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 /// Check if the current client is already authorized
@ -90,12 +107,13 @@ class FitbitClient {
/// - viewController: the source controller to create the new Safari view /// - viewController: the source controller to create the new Safari view
/// - success: Callback to be executed on success /// - success: Callback to be executed on success
/// - failure: Callback to be executed on failure /// - 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 // Set webview handler
self.oauthClient.authorizeURLHandler = SafariURLHandler( self.oauthClient.authorizeURLHandler = SafariURLHandler(
viewController: viewController, viewController: viewController,
oauthSwift: self.oauthClient oauthSwift: self.oauthClient
) )
// Authorize // Authorize
let _ = self.oauthClient.authorize( let _ = self.oauthClient.authorize(
withCallbackURL: URL(string: self.callbackUrl)!, withCallbackURL: URL(string: self.callbackUrl)!,
@ -108,19 +126,18 @@ class FitbitClient {
let scope = parameters["scope"] as? String let scope = parameters["scope"] as? String
else { else {
NSLog("Invalid authorization response") NSLog("Invalid authorization response")
failure() callback(nil, FitKitRequestError.invalidAuthorization)
return return
} }
NSLog("Authorized scope: \(scope)") NSLog("Authorized scope: \(scope)")
NSLog("Succesfully authenticated with credentials \(credential.oauthToken)") NSLog("Succesfully authenticated with credentials \(credential.oauthToken)")
let _ = self.storeCredential(credential) let _ = self.storeCredential(credential)
success() callback(scope, nil)
}, },
failure: { failure: {
error in error in
// TODO: Maybe return the error NSLog("Error authorizing \(error.localizedDescription)")
NSLog(error.localizedDescription) callback(nil, error)
failure()
} }
) )
} }

View File

@ -23,7 +23,7 @@ class ViewController: UIViewController {
} }
override func viewDidAppear(_ animated: Bool) { override func viewDidAppear(_ animated: Bool) {
authorize() ensureAuthorized(callback: self.updateView)
} }
override func didReceiveMemoryWarning() { override func didReceiveMemoryWarning() {
@ -34,35 +34,53 @@ class ViewController: UIViewController {
fileprivate func updateAuthorizedLabel() { fileprivate func updateAuthorizedLabel() {
if self.client.isAuthorized() { if self.client.isAuthorized() {
self.loggedIn.text = "Logged In" self.loggedIn.text = "Logged In"
//self.loginButton.currentTitle = "Log Out"
} else { } else {
self.loggedIn.text = "Not Logged In" self.loggedIn.text = "Not Logged In"
//self.loginButton.currentTitle = "Log In"
} }
} }
fileprivate func authorize() { /// 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() { HealthKitHelper.authorizeHealthKit() {
result, error in result, error in
// TODO: Do something with the result and error // TODO: Do something with the result and error
if !self.client.isAuthorized() { if error != nil {
self.client.authorize(viewController: self, success: self.authorized, failure: self.unauthroized) callback(false)
} else { } else {
// TODO: this should be made significantly more clear callback(true)
self.displayWeight() }
} }
} }
} }
fileprivate func authorized() { fileprivate func updateView(authSuccess: Bool) {
NSLog("We have Fitbit auth!!!") self.updateAuthorizedLabel()
updateAuthorizedLabel() if authSuccess {
self.displayWeight() self.syncWeights()
}
} }
fileprivate func unauthroized() { /// Synchronizes weights from Fitbit to HealthKit
NSLog("No auth. Booo!!!") func syncWeights() {
}
func displayWeight() {
let today = Date() let today = Date()
let lastMonth = Calendar.current.date(byAdding: .day, value: -31, to: today)! let lastMonth = Calendar.current.date(byAdding: .day, value: -31, to: today)!
self.client.getWeight(start: lastMonth, end: today) { self.client.getWeight(start: lastMonth, end: today) {
@ -70,6 +88,7 @@ class ViewController: UIViewController {
fbWeights, error in fbWeights, error in
if let error = error { if let error = error {
NSLog("Unable to get weights: \(error)") NSLog("Unable to get weights: \(error)")
self.outputText.text = "Error getting weights"
return return
} }