Clean up of view, passing back scopes, add icon
This commit is contained in:
parent
4b3e0898c7
commit
bb34a2635f
@ -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"
|
||||
},
|
||||
{
|
||||
|
BIN
FitKit/Assets.xcassets/AppIcon.appiconset/icon-120.png
Normal file
BIN
FitKit/Assets.xcassets/AppIcon.appiconset/icon-120.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.0 KiB |
BIN
FitKit/Assets.xcassets/AppIcon.appiconset/icon-180.png
Normal file
BIN
FitKit/Assets.xcassets/AppIcon.appiconset/icon-180.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.9 KiB |
6
FitKit/Assets.xcassets/Contents.json
Normal file
6
FitKit/Assets.xcassets/Contents.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
@ -27,14 +27,14 @@
|
||||
</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">
|
||||
<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"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<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"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<rect key="frame" x="16" y="152" width="343" height="305"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
@ -44,7 +44,7 @@
|
||||
<accessibility key="accessibilityConfiguration">
|
||||
<accessibilityTraits key="traits" notEnabled="YES"/>
|
||||
</accessibility>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
<viewLayoutGuide key="safeArea" id="OGN-ry-fYp"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="loggedIn" destination="CoH-cd-flw" id="SUh-wA-KNF"/>
|
||||
|
@ -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)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user