Clean up of view, passing back scopes, add icon
This commit is contained in:
parent
4b3e0898c7
commit
bb34a2635f
@ -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"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
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>
|
</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"/>
|
||||||
|
@ -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,10 +70,21 @@ 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
|
||||||
///
|
///
|
||||||
/// - Returns: True if the current client can make requests
|
/// - 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
|
/// - 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()
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
HealthKitHelper.authorizeHealthKit() {
|
fileprivate func ensureAuthorized(callback: @escaping (Bool) -> Void) {
|
||||||
result, error in
|
// If already authroized, we can short circuit
|
||||||
// TODO: Do something with the result and error
|
if self.client.isAuthorized() {
|
||||||
if !self.client.isAuthorized() {
|
callback(true)
|
||||||
self.client.authorize(viewController: self, success: self.authorized, failure: self.unauthroized)
|
return
|
||||||
} else {
|
}
|
||||||
// TODO: this should be made significantly more clear
|
// Authorize the client
|
||||||
self.displayWeight()
|
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() {
|
fileprivate func updateView(authSuccess: Bool) {
|
||||||
NSLog("We have Fitbit auth!!!")
|
self.updateAuthorizedLabel()
|
||||||
updateAuthorizedLabel()
|
if authSuccess {
|
||||||
self.displayWeight()
|
self.syncWeights()
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func unauthroized() {
|
|
||||||
NSLog("No auth. Booo!!!")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func displayWeight() {
|
/// Synchronizes weights from Fitbit to HealthKit
|
||||||
|
func syncWeights() {
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user