How to get steps from Fitbit in swift

How to get steps from Fitbit

First of all setup Fitbit account in https://dev.fitbit.com/

Get Client id & Client Secret from Fitbit dashboard

set Identifier and URL Scheme

-> identifier = your bundle id

-> URL Schemes : your web url

Create model for Fitbit credential

class FitbitAPI {
	
	static let sharedInstance: FitbitAPI = FitbitAPI()
	
	static let baseAPIURL = URL(string:"https://api.fitbit.com/1")
	
	func authorize(with token: String) {
		let sessionConfiguration = URLSessionConfiguration.default
		var headers = sessionConfiguration.httpAdditionalHeaders ?? [:]
		headers["Authorization"] = "Bearer \(token)"
		sessionConfiguration.httpAdditionalHeaders = headers
		session = URLSession(configuration: sessionConfiguration)
	}
	
	var session: URLSession?
}

class ViewController: UIViewController,AuthenticationProtocol {
    
    var authenticationController: AuthenticationController?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        authenticationController = AuthenticationController(delegate: self)
    }
    
    
    private func retrivedataFromFitbit() {
        
        if let authToken = UserDefaults.standard.string(forKey: "authToken") {
              FitbitAPI.sharedInstance.authorize(with: authToken)
          
              fetchFitbitActivity.fetchFitbitActivity(date: Date()) { (steps) in
              print(steps ?? 0)
            }
        }
    }
}

Fetch Fitbit activity model

import Foundation


import SafariServices
import AuthenticationServices

typealias AuthHandlerCompletion = (URL?, Error?) -> Void

class AuthContextProvider: NSObject {

    private weak var anchor: ASPresentationAnchor!

    init(_ anchor: ASPresentationAnchor) {
        self.anchor = anchor
    }

}

extension AuthContextProvider: ASWebAuthenticationPresentationContextProviding {

    @available(iOS 12.0, *)
    func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
        return anchor
    }

}

protocol AuthHandlerType: class {
    var session: NSObject? { get set }
    var contextProvider: AuthContextProvider? { get set }
    func auth(url: URL, callbackScheme: String, completion: @escaping AuthHandlerCompletion)
}

extension AuthHandlerType {

    func auth(url: URL, callbackScheme: String, completion: @escaping AuthHandlerCompletion) {
        if #available(iOS 12, *) {
            let session = ASWebAuthenticationSession(url: url, callbackURLScheme: callbackScheme) {
                url, error in
                completion(url, error)
            }
            if #available(iOS 13.0, *) {
                session.presentationContextProvider = contextProvider
            } else {
                // Fallback on earlier versions
            }
            session.start()
            self.session = session
        } else {
            let session = SFAuthenticationSession(url: url, callbackURLScheme: callbackScheme) {
                url, error in
                completion(url, error)
            }
            session.start()
            self.session = session
        }
    }

}

struct Constants {

    static let authUrl = URL(string: "https://www.fitbit.com/oauth2/authorize")
    static let responseType = "code"
    static let clientId = "your client id"
    static let redirectScheme = "your web url://m"
    static let redirectUrl = "\(redirectScheme)fitbit/auth"
    static let scope = ["activity", "heartrate", "location", "nutrition", "profile", "settings", "sleep", "social", "weight"]
    static let expires = "604800"

    private init() {}

}

class Model: AuthHandlerType {

    var session: NSObject? = nil
    var contextProvider: AuthContextProvider?

    func auth(_ completion: @escaping ((String?, Error?) -> Void)) {
        guard let authUrl = Constants.authUrl else {
            completion(nil, nil)

            return
        }

        var urlComponents = URLComponents(url: authUrl, resolvingAgainstBaseURL: false)
        urlComponents?.queryItems = [
            URLQueryItem(name: "response_type", value: Constants.responseType),
            URLQueryItem(name: "client_id", value: Constants.clientId),
            URLQueryItem(name: "redirect_url", value: Constants.redirectUrl),
            URLQueryItem(name: "scope", value: Constants.scope.joined(separator: " ")),
            URLQueryItem(name: "expires_in", value: String(Constants.expires))
        ]

        guard let url = urlComponents?.url else {
            completion(nil, nil)

            return
        }

        auth(url: url, callbackScheme: Constants.redirectScheme) {
            url, error in
            if error != nil {
                completion(nil, error)
            } else if let `url` = url {
                guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
                      let item = components.queryItems?.first(where: { $0.name == "code" }),
                      let code = item.value else {
                    completion(nil, nil)

                    return
                }
                completion(code, nil)
            }
        }
    }

}


class fetchFitbitActivity {
    
    static func fetchFitbitActivity(date : Date , callback: @escaping (Int)->()){
    
    //static func fetchFitbitActivity(arg: Bool, completion: (Bool) -> ()) {
        
        let dateformatter = DateFormatter()
        dateformatter.dateFormat = "yyyy-MM-dd"
        dateformatter.timeZone = TimeZone(identifier: "UTC")
        dateformatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") as Locale?
        var datePath = dateformatter.string(from: date)
        datePath = datePath + ".json"
        
        print(datePath)
        guard let session = FitbitAPI.sharedInstance.session,
            let stepURL = URL(string: "https://api.fitbit.com/1/user/-/activities/date/\(datePath)") else {
                
                callback(0)
        
                return
        }
         

        
        let dataTask = session.dataTask(with: stepURL) { (data, response, error) in
            guard let response = response as? HTTPURLResponse, response.statusCode < 300 else {

                callback(0)
                return
            }
            do {
                let jsonDecoder = JSONDecoder()
                guard let data = data,
                    let dictionary = (try jsonDecoder.decode(Json4Swift_Base_Fitbit_Exercise.self, from: data)) as? Json4Swift_Base_Fitbit_Exercise else {

                        callback(0)
                        return
                }
                //let Activities = dictionary.activities ?? []
                let TotalStepCount = dictionary.summary?.steps ?? 0
                print(TotalStepCount)
                callback(TotalStepCount)
                
            }catch{
                print(error.localizedDescription)
            }

        }
        dataTask.resume()
    }
}

Fitbit data Model

import Foundation
struct Json4Swift_Base_Fitbit_Exercise : Codable {
	let activities : [Activities]?
	let goals : Goals?
	let summary : Summary?

	enum CodingKeys: String, CodingKey {

		case activities = "activities"
		case goals = "goals"
		case summary = "summary"
	}

	init(from decoder: Decoder) throws {
		let values = try decoder.container(keyedBy: CodingKeys.self)
		activities = try values.decodeIfPresent([Activities].self, forKey: .activities)
		goals = try values.decodeIfPresent(Goals.self, forKey: .goals)
		summary = try values.decodeIfPresent(Summary.self, forKey: .summary)
	}

}

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.