[{"data":1,"prerenderedAt":1441},["ShallowReactive",2],{"Categories":3,"NavIndexCategoriesCountFooter":203,"content-\u002F2020\u002F03\u002F18\u002Ffetching-the-token\u002F":204},[4,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,68,70,71,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202],{"category":5},"System Administration",{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":27},"Software Development",{"category":5},{"category":5},{"category":5},{"category":5},{"category":27},{"category":27},{"category":5},{"category":5},{"category":5},{"category":27},{"category":5},{"category":5},{"category":5},{"category":27},{"category":27},{"category":27},{"category":27},{"category":5},{"category":5},{"category":5},{"category":27},{"category":27},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":27},{"category":5},{"category":5},{"category":27},{"category":27},{"category":27},{"category":27},{"category":5},{"category":27},{"category":27},{"category":67},"Drones & RC",{"category":69},"DIY Projects",{"category":67},{"category":72},"Photography",{"category":69},{"category":69},{"category":69},{"category":67},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":67},{"category":69},{"category":69},{"category":67},{"category":67},{"category":72},{"category":72},{"category":72},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":5},{"category":5},{"category":72},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":5},{"category":67},{"category":67},{"category":72},{"category":72},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":72},{"category":67},{"category":138},"3D Printing - Laser Cutting - CNC",{"category":138},{"category":138},{"category":138},{"category":138},{"category":138},{"category":138},{"category":138},{"category":138},{"category":138},{"category":138},{"category":138},{"category":5},{"category":138},{"category":27},{"category":27},{"category":138},{"category":138},{"category":72},{"category":158},"Photography,3D Printing - Laser Cutting - CNC",{"category":27},{"category":27},{"category":69},{"category":27},{"category":27},{"category":27},{"category":27},{"category":5},{"category":67},{"category":5},{"category":5},{"category":27},{"category":27},{"category":27},{"category":27},{"category":27},{"category":69},{"category":27},{"category":27},{"category":27},{"category":27},{"category":181},"Home Assistant",{"category":181},{"category":72},{"category":27},{"category":27},{"category":72},{"category":138},{"category":5},{"category":72},{"category":72},{"category":138},{"category":27},{"category":181},{"category":181},{"category":72},{"category":72},{"category":72},{"category":72},{"category":72},{"category":72},{"category":72},{"category":72},191,{"id":205,"title":206,"body":207,"category":27,"date":1425,"description":213,"embedImage":1426,"extension":1427,"image":1426,"intro":1428,"meta":1429,"navigation":343,"path":1430,"seo":1431,"series":1432,"sitemap":1433,"stem":1434,"tags":1435,"__hash__":1440},"content\u002F2020\u002F03\u002F18\u002Ffetching-the-token.md","Fetching the token",{"type":208,"value":209,"toc":1417},"minimark",[210,214,217,220,225,228,231,244,247,258,265,268,271,274,422,425,430,443,446,568,572,575,578,1250,1252,1256,1259,1262,1346,1353,1392,1395,1397,1401,1404,1406,1413],[211,212,213],"p",{},"The app so far has the ability to read its configuration and to check that you are who you say you are on devices that support touch or face id.",[211,215,216],{},"The next step is to get an access token from the S'banken API.",[218,219],"hr",{},[221,222,224],"h2",{"id":223},"tokens","Tokens",[211,226,227],{},"S'banken is using a standard authentication mechanism - OAuth client credentials.",[211,229,230],{},"To make this call we need to send a POST request to the token endpoint with the following headers:",[232,233,234,238,241],"ul",{},[235,236,237],"li",{},"Accept: application\u002Fjson",[235,239,240],{},"Content-Type: application\u002Fx-www-form-urlencoded",[235,242,243],{},"Authorization: BASIC",[211,245,246],{},"The body must be",[248,249,254],"pre",{"className":250,"code":252,"language":253},[251],"language-text","grant_type=client_credentials\n","text",[255,256,252],"code",{"__ignoreMap":257},"",[211,259,260,261,264],{},"So - that authorization header. This call uses basic auth - which is a base 64 encoded version of ",[255,262,263],{},"username:password",". Here - username is the clientId and password is the clientSecret. But we need to do one more step - url encode each part before we base 64 encode it.",[211,266,267],{},"Let's create a little utility class with a static function. It will take a config object and a completion callback (the request will be async).",[211,269,270],{},"Add a new file - TokenService.swift. At the top - add an import for Alamofire",[211,272,273],{},"We'll start by creating a Codable representation of the response:",[248,275,279],{"className":276,"code":277,"language":278,"meta":257,"style":257},"language-swift shiki shiki-themes github-dark","struct Token : Decodable {\n    let accessToken: String\n    let expiresIn: Int\n    let tokenType: String\n\n    enum CodingKeys: String, CodingKey {\n        case accessToken = \"access_token\"\n        case expiresIn = \"expires_in\"\n        case tokenType = \"token_type\"\n    }\n}\n","swift",[255,280,281,304,317,328,338,345,368,384,397,410,416],{"__ignoreMap":257},[282,283,286,290,294,298,301],"span",{"class":284,"line":285},"line",1,[282,287,289],{"class":288},"snl16","struct",[282,291,293],{"class":292},"svObZ"," Token",[282,295,297],{"class":296},"s95oV"," : ",[282,299,300],{"class":292},"Decodable ",[282,302,303],{"class":296},"{\n",[282,305,307,310,313],{"class":284,"line":306},2,[282,308,309],{"class":288},"    let",[282,311,312],{"class":296}," accessToken: ",[282,314,316],{"class":315},"sDLfK","String\n",[282,318,320,322,325],{"class":284,"line":319},3,[282,321,309],{"class":288},[282,323,324],{"class":296}," expiresIn: ",[282,326,327],{"class":315},"Int\n",[282,329,331,333,336],{"class":284,"line":330},4,[282,332,309],{"class":288},[282,334,335],{"class":296}," tokenType: ",[282,337,316],{"class":315},[282,339,341],{"class":284,"line":340},5,[282,342,344],{"emptyLinePlaceholder":343},true,"\n",[282,346,348,351,354,357,360,363,366],{"class":284,"line":347},6,[282,349,350],{"class":288},"    enum",[282,352,353],{"class":292}," CodingKeys",[282,355,356],{"class":296},": ",[282,358,359],{"class":315},"String",[282,361,362],{"class":296},", ",[282,364,365],{"class":292},"CodingKey ",[282,367,303],{"class":296},[282,369,371,374,377,380],{"class":284,"line":370},7,[282,372,373],{"class":288},"        case",[282,375,376],{"class":315}," accessToken",[282,378,379],{"class":288}," =",[282,381,383],{"class":382},"sU2Wk"," \"access_token\"\n",[282,385,387,389,392,394],{"class":284,"line":386},8,[282,388,373],{"class":288},[282,390,391],{"class":315}," expiresIn",[282,393,379],{"class":288},[282,395,396],{"class":382}," \"expires_in\"\n",[282,398,400,402,405,407],{"class":284,"line":399},9,[282,401,373],{"class":288},[282,403,404],{"class":315}," tokenType",[282,406,379],{"class":288},[282,408,409],{"class":382}," \"token_type\"\n",[282,411,413],{"class":284,"line":412},10,[282,414,415],{"class":296},"    }\n",[282,417,419],{"class":284,"line":418},11,[282,420,421],{"class":296},"}\n",[211,423,424],{},"This will be used when parsing the response from the API.",[426,427,429],"h3",{"id":428},"percent-encoding","Percent Encoding",[211,431,432,433,442],{},"Now - encoding the username and password fields. Swift strings have a method addingPercentEncoding which will do this for us - given the correct allowed CharacterSet. I found that none of the default available character sets worked as I wanted so I took a peek at S'banken's own swift API implementation: ",[434,435,436],"a",{"href":436,"rel":437,"target":441},"https:\u002F\u002Fgithub.com\u002FSbanken\u002Fsbankenclient-ios\u002Fblob\u002Fmaster\u002FSbankenClient\u002FSbankenClient.swift#L257-L261",[438,439,440],"nofollow","noopener","noreferer","_blank"," and found that they are constructing a character set - so - let's do that.",[211,444,445],{},"We'll add it as an extension onto String:",[248,447,449],{"className":276,"code":448,"language":278,"meta":257,"style":257},"extension String {\n    public func encodeForAuth() -> String? {\n        let characterSet = NSMutableCharacterSet.alphanumeric()\n        characterSet.addCharacters(in: \"-_.!~*'()\")\n\n        return self.addingPercentEncoding(withAllowedCharacters: characterSet as CharacterSet)\n    }\n}\n",[255,450,451,462,486,506,528,532,560,564],{"__ignoreMap":257},[282,452,453,456,459],{"class":284,"line":285},[282,454,455],{"class":288},"extension",[282,457,458],{"class":315}," String",[282,460,461],{"class":296}," {\n",[282,463,464,467,470,473,476,479,481,484],{"class":284,"line":306},[282,465,466],{"class":288},"    public",[282,468,469],{"class":288}," func",[282,471,472],{"class":292}," encodeForAuth",[282,474,475],{"class":296},"() ",[282,477,478],{"class":288},"->",[282,480,458],{"class":315},[282,482,483],{"class":288},"?",[282,485,461],{"class":296},[282,487,488,491,494,497,500,503],{"class":284,"line":319},[282,489,490],{"class":288},"        let",[282,492,493],{"class":296}," characterSet ",[282,495,496],{"class":288},"=",[282,498,499],{"class":296}," NSMutableCharacterSet.",[282,501,502],{"class":315},"alphanumeric",[282,504,505],{"class":296},"()\n",[282,507,508,511,514,517,520,522,525],{"class":284,"line":330},[282,509,510],{"class":296},"        characterSet.",[282,512,513],{"class":315},"addCharacters",[282,515,516],{"class":296},"(",[282,518,519],{"class":315},"in",[282,521,356],{"class":296},[282,523,524],{"class":382},"\"-_.!~*'()\"",[282,526,527],{"class":296},")\n",[282,529,530],{"class":284,"line":340},[282,531,344],{"emptyLinePlaceholder":343},[282,533,534,537,540,543,546,548,551,554,557],{"class":284,"line":347},[282,535,536],{"class":288},"        return",[282,538,539],{"class":315}," self",[282,541,542],{"class":296},".",[282,544,545],{"class":315},"addingPercentEncoding",[282,547,516],{"class":296},[282,549,550],{"class":315},"withAllowedCharacters",[282,552,553],{"class":296},": characterSet ",[282,555,556],{"class":288},"as",[282,558,559],{"class":296}," CharacterSet)\n",[282,561,562],{"class":284,"line":370},[282,563,415],{"class":296},[282,565,566],{"class":284,"line":386},[282,567,421],{"class":296},[426,569,571],{"id":570},"request","Request",[211,573,574],{},"Finally we can start to construct our request.",[211,576,577],{},"We create a TokenService class with a static getToken function:",[248,579,581],{"className":276,"code":580,"language":278,"meta":257,"style":257},"class TokenService {\n    public static func getToken(config: Config, onComplete: @escaping (_ accessToken: String?) -> Void) {\n\n        \u002F\u002F Get username param encoded\n        guard let username = config.clientId.encodeForAuth() else {\n            onComplete(nil)\n\n            return\n        }\n\n        \u002F\u002F Get password param encoded\n        guard let password = config.clientSecret.encodeForAuth() else {\n            onComplete(nil)\n\n            return\n        }\n\n        \u002F\u002F Construct the basic auth string\n        let basicAuth = Data(\"\\(username):\\(password)\".utf8).base64EncodedString()\n\n        \u002F\u002F Headers required\n        let headers: HTTPHeaders = [\n            \"Authorization\": \"Basic \\(basicAuth)\",\n            \"Accept\": \"application\u002Fjson\",\n            \"Content-Type\": \"application\u002Fx-www-form-urlencoded\"\n        ]\n\n        \u002F\u002F Body params\n        let parameters = [\"grant_type\": \"client_credentials\"]\n\n        \u002F\u002F Construct the request - setting the body to x-www-form-urlencoded\n        let request = AF.request(\"https:\u002F\u002Fauth.sbanken.no\u002Fidentityserver\u002Fconnect\u002Ftoken\",\n                                 method: .post,\n                                 parameters: parameters,\n                                 encoding: URLEncoding.httpBody,\n                                 headers: headers)\n\n        let decoder = JSONDecoder()\n\n        \u002F\u002F Call the API\n        request.responseDecodable(of: Token.self, decoder: decoder) { (response) in\n            if let error = response.error {\n                \u002F\u002F It went wrong\n                print(\"Unable to fetch token \\(response) \\(error.localizedDescription)\")\n\n                onComplete(nil)\n\n                return\n            }\n\n            guard let token = response.value else {\n                \u002F\u002F We got an answer but could not parse it\n                print(\"Unable to read token\")\n\n                onComplete(nil)\n\n                return\n            }\n\n            \u002F\u002F Token received\n            onComplete(token.accessToken)\n        }\n    }\n}\n",[255,582,583,593,639,643,649,675,687,691,696,701,705,710,733,744,749,754,759,764,770,812,817,823,836,855,868,879,885,890,896,920,925,931,953,962,971,980,989,994,1009,1014,1020,1051,1072,1078,1105,1110,1122,1127,1133,1139,1144,1167,1173,1185,1190,1201,1206,1211,1216,1221,1227,1235,1240,1245],{"__ignoreMap":257},[282,584,585,588,591],{"class":284,"line":285},[282,586,587],{"class":288},"class",[282,589,590],{"class":292}," TokenService",[282,592,461],{"class":296},[282,594,595,597,600,602,605,607,610,613,616,618,621,624,626,628,631,633,636],{"class":284,"line":306},[282,596,466],{"class":288},[282,598,599],{"class":288}," static",[282,601,469],{"class":288},[282,603,604],{"class":292}," getToken",[282,606,516],{"class":296},[282,608,609],{"class":292},"config",[282,611,612],{"class":296},": Config, ",[282,614,615],{"class":292},"onComplete",[282,617,356],{"class":296},[282,619,620],{"class":288},"@escaping",[282,622,623],{"class":296}," (_ accessToken: ",[282,625,359],{"class":315},[282,627,483],{"class":288},[282,629,630],{"class":296},") ",[282,632,478],{"class":288},[282,634,635],{"class":315}," Void",[282,637,638],{"class":296},") {\n",[282,640,641],{"class":284,"line":319},[282,642,344],{"emptyLinePlaceholder":343},[282,644,645],{"class":284,"line":330},[282,646,648],{"class":647},"sAwPA","        \u002F\u002F Get username param encoded\n",[282,650,651,654,657,660,662,665,668,670,673],{"class":284,"line":340},[282,652,653],{"class":288},"        guard",[282,655,656],{"class":288}," let",[282,658,659],{"class":296}," username ",[282,661,496],{"class":288},[282,663,664],{"class":296}," config.clientId.",[282,666,667],{"class":315},"encodeForAuth",[282,669,475],{"class":296},[282,671,672],{"class":288},"else",[282,674,461],{"class":296},[282,676,677,680,682,685],{"class":284,"line":347},[282,678,679],{"class":315},"            onComplete",[282,681,516],{"class":296},[282,683,684],{"class":315},"nil",[282,686,527],{"class":296},[282,688,689],{"class":284,"line":370},[282,690,344],{"emptyLinePlaceholder":343},[282,692,693],{"class":284,"line":386},[282,694,695],{"class":288},"            return\n",[282,697,698],{"class":284,"line":399},[282,699,700],{"class":296},"        }\n",[282,702,703],{"class":284,"line":412},[282,704,344],{"emptyLinePlaceholder":343},[282,706,707],{"class":284,"line":418},[282,708,709],{"class":647},"        \u002F\u002F Get password param encoded\n",[282,711,713,715,717,720,722,725,727,729,731],{"class":284,"line":712},12,[282,714,653],{"class":288},[282,716,656],{"class":288},[282,718,719],{"class":296}," password ",[282,721,496],{"class":288},[282,723,724],{"class":296}," config.clientSecret.",[282,726,667],{"class":315},[282,728,475],{"class":296},[282,730,672],{"class":288},[282,732,461],{"class":296},[282,734,736,738,740,742],{"class":284,"line":735},13,[282,737,679],{"class":315},[282,739,516],{"class":296},[282,741,684],{"class":315},[282,743,527],{"class":296},[282,745,747],{"class":284,"line":746},14,[282,748,344],{"emptyLinePlaceholder":343},[282,750,752],{"class":284,"line":751},15,[282,753,695],{"class":288},[282,755,757],{"class":284,"line":756},16,[282,758,700],{"class":296},[282,760,762],{"class":284,"line":761},17,[282,763,344],{"emptyLinePlaceholder":343},[282,765,767],{"class":284,"line":766},18,[282,768,769],{"class":647},"        \u002F\u002F Construct the basic auth string\n",[282,771,773,775,778,780,783,785,788,791,794,797,799,801,804,807,810],{"class":284,"line":772},19,[282,774,490],{"class":288},[282,776,777],{"class":296}," basicAuth ",[282,779,496],{"class":288},[282,781,782],{"class":315}," Data",[282,784,516],{"class":296},[282,786,787],{"class":382},"\"",[282,789,790],{"class":382},"\\(username)",[282,792,793],{"class":382},":",[282,795,796],{"class":382},"\\(password)",[282,798,787],{"class":382},[282,800,542],{"class":296},[282,802,803],{"class":315},"utf8",[282,805,806],{"class":296},").",[282,808,809],{"class":315},"base64EncodedString",[282,811,505],{"class":296},[282,813,815],{"class":284,"line":814},20,[282,816,344],{"emptyLinePlaceholder":343},[282,818,820],{"class":284,"line":819},21,[282,821,822],{"class":647},"        \u002F\u002F Headers required\n",[282,824,826,828,831,833],{"class":284,"line":825},22,[282,827,490],{"class":288},[282,829,830],{"class":296}," headers: HTTPHeaders ",[282,832,496],{"class":288},[282,834,835],{"class":296}," [\n",[282,837,839,842,844,847,850,852],{"class":284,"line":838},23,[282,840,841],{"class":382},"            \"Authorization\"",[282,843,793],{"class":288},[282,845,846],{"class":382}," \"Basic ",[282,848,849],{"class":382},"\\(basicAuth)",[282,851,787],{"class":382},[282,853,854],{"class":296},",\n",[282,856,858,861,863,866],{"class":284,"line":857},24,[282,859,860],{"class":382},"            \"Accept\"",[282,862,793],{"class":288},[282,864,865],{"class":382}," \"application\u002Fjson\"",[282,867,854],{"class":296},[282,869,871,874,876],{"class":284,"line":870},25,[282,872,873],{"class":382},"            \"Content-Type\"",[282,875,793],{"class":288},[282,877,878],{"class":382}," \"application\u002Fx-www-form-urlencoded\"\n",[282,880,882],{"class":284,"line":881},26,[282,883,884],{"class":296},"        ]\n",[282,886,888],{"class":284,"line":887},27,[282,889,344],{"emptyLinePlaceholder":343},[282,891,893],{"class":284,"line":892},28,[282,894,895],{"class":647},"        \u002F\u002F Body params\n",[282,897,899,901,904,906,909,912,914,917],{"class":284,"line":898},29,[282,900,490],{"class":288},[282,902,903],{"class":296}," parameters ",[282,905,496],{"class":288},[282,907,908],{"class":296}," [",[282,910,911],{"class":382},"\"grant_type\"",[282,913,793],{"class":288},[282,915,916],{"class":382}," \"client_credentials\"",[282,918,919],{"class":296},"]\n",[282,921,923],{"class":284,"line":922},30,[282,924,344],{"emptyLinePlaceholder":343},[282,926,928],{"class":284,"line":927},31,[282,929,930],{"class":647},"        \u002F\u002F Construct the request - setting the body to x-www-form-urlencoded\n",[282,932,934,936,939,941,944,946,948,951],{"class":284,"line":933},32,[282,935,490],{"class":288},[282,937,938],{"class":296}," request ",[282,940,496],{"class":288},[282,942,943],{"class":296}," AF.",[282,945,570],{"class":315},[282,947,516],{"class":296},[282,949,950],{"class":382},"\"https:\u002F\u002Fauth.sbanken.no\u002Fidentityserver\u002Fconnect\u002Ftoken\"",[282,952,854],{"class":296},[282,954,956,959],{"class":284,"line":955},33,[282,957,958],{"class":315},"                                 method",[282,960,961],{"class":296},": .post,\n",[282,963,965,968],{"class":284,"line":964},34,[282,966,967],{"class":315},"                                 parameters",[282,969,970],{"class":296},": parameters,\n",[282,972,974,977],{"class":284,"line":973},35,[282,975,976],{"class":315},"                                 encoding",[282,978,979],{"class":296},": URLEncoding.httpBody,\n",[282,981,983,986],{"class":284,"line":982},36,[282,984,985],{"class":315},"                                 headers",[282,987,988],{"class":296},": headers)\n",[282,990,992],{"class":284,"line":991},37,[282,993,344],{"emptyLinePlaceholder":343},[282,995,997,999,1002,1004,1007],{"class":284,"line":996},38,[282,998,490],{"class":288},[282,1000,1001],{"class":296}," decoder ",[282,1003,496],{"class":288},[282,1005,1006],{"class":315}," JSONDecoder",[282,1008,505],{"class":296},[282,1010,1012],{"class":284,"line":1011},39,[282,1013,344],{"emptyLinePlaceholder":343},[282,1015,1017],{"class":284,"line":1016},40,[282,1018,1019],{"class":647},"        \u002F\u002F Call the API\n",[282,1021,1023,1026,1029,1031,1034,1037,1040,1042,1045,1048],{"class":284,"line":1022},41,[282,1024,1025],{"class":296},"        request.",[282,1027,1028],{"class":315},"responseDecodable",[282,1030,516],{"class":296},[282,1032,1033],{"class":315},"of",[282,1035,1036],{"class":296},": Token.",[282,1038,1039],{"class":288},"self",[282,1041,362],{"class":296},[282,1043,1044],{"class":315},"decoder",[282,1046,1047],{"class":296},": decoder) { (response) ",[282,1049,1050],{"class":288},"in\n",[282,1052,1054,1057,1059,1062,1064,1067,1070],{"class":284,"line":1053},42,[282,1055,1056],{"class":288},"            if",[282,1058,656],{"class":288},[282,1060,1061],{"class":296}," error ",[282,1063,496],{"class":288},[282,1065,1066],{"class":296}," response.",[282,1068,1069],{"class":315},"error",[282,1071,461],{"class":296},[282,1073,1075],{"class":284,"line":1074},43,[282,1076,1077],{"class":647},"                \u002F\u002F It went wrong\n",[282,1079,1081,1084,1086,1089,1092,1095,1098,1101,1103],{"class":284,"line":1080},44,[282,1082,1083],{"class":315},"                print",[282,1085,516],{"class":296},[282,1087,1088],{"class":382},"\"Unable to fetch token ",[282,1090,1091],{"class":382},"\\(response)",[282,1093,1094],{"class":382}," \\(error.",[282,1096,1097],{"class":296},"localizedDescription",[282,1099,1100],{"class":382},")",[282,1102,787],{"class":382},[282,1104,527],{"class":296},[282,1106,1108],{"class":284,"line":1107},45,[282,1109,344],{"emptyLinePlaceholder":343},[282,1111,1113,1116,1118,1120],{"class":284,"line":1112},46,[282,1114,1115],{"class":315},"                onComplete",[282,1117,516],{"class":296},[282,1119,684],{"class":315},[282,1121,527],{"class":296},[282,1123,1125],{"class":284,"line":1124},47,[282,1126,344],{"emptyLinePlaceholder":343},[282,1128,1130],{"class":284,"line":1129},48,[282,1131,1132],{"class":288},"                return\n",[282,1134,1136],{"class":284,"line":1135},49,[282,1137,1138],{"class":296},"            }\n",[282,1140,1142],{"class":284,"line":1141},50,[282,1143,344],{"emptyLinePlaceholder":343},[282,1145,1147,1150,1152,1155,1157,1159,1162,1165],{"class":284,"line":1146},51,[282,1148,1149],{"class":288},"            guard",[282,1151,656],{"class":288},[282,1153,1154],{"class":296}," token ",[282,1156,496],{"class":288},[282,1158,1066],{"class":296},[282,1160,1161],{"class":315},"value",[282,1163,1164],{"class":288}," else",[282,1166,461],{"class":296},[282,1168,1170],{"class":284,"line":1169},52,[282,1171,1172],{"class":647},"                \u002F\u002F We got an answer but could not parse it\n",[282,1174,1176,1178,1180,1183],{"class":284,"line":1175},53,[282,1177,1083],{"class":315},[282,1179,516],{"class":296},[282,1181,1182],{"class":382},"\"Unable to read token\"",[282,1184,527],{"class":296},[282,1186,1188],{"class":284,"line":1187},54,[282,1189,344],{"emptyLinePlaceholder":343},[282,1191,1193,1195,1197,1199],{"class":284,"line":1192},55,[282,1194,1115],{"class":315},[282,1196,516],{"class":296},[282,1198,684],{"class":315},[282,1200,527],{"class":296},[282,1202,1204],{"class":284,"line":1203},56,[282,1205,344],{"emptyLinePlaceholder":343},[282,1207,1209],{"class":284,"line":1208},57,[282,1210,1132],{"class":288},[282,1212,1214],{"class":284,"line":1213},58,[282,1215,1138],{"class":296},[282,1217,1219],{"class":284,"line":1218},59,[282,1220,344],{"emptyLinePlaceholder":343},[282,1222,1224],{"class":284,"line":1223},60,[282,1225,1226],{"class":647},"            \u002F\u002F Token received\n",[282,1228,1230,1232],{"class":284,"line":1229},61,[282,1231,679],{"class":315},[282,1233,1234],{"class":296},"(token.accessToken)\n",[282,1236,1238],{"class":284,"line":1237},62,[282,1239,700],{"class":296},[282,1241,1243],{"class":284,"line":1242},63,[282,1244,415],{"class":296},[282,1246,1248],{"class":284,"line":1247},64,[282,1249,421],{"class":296},[218,1251],{},[221,1253,1255],{"id":1254},"triggering-the-get-token-call","Triggering the get token call",[211,1257,1258],{},"For now - in the main view - in the onAppear - we load config and ensure that you are logged in.",[211,1260,1261],{},"Add the following function to ContentView - it will call the service if the config is available and then print to console what we got back",[248,1263,1265],{"className":276,"code":1264,"language":278,"meta":257,"style":257},"    func getToken() {\n        if let config = self.config {\n            TokenService.getToken(config: config) { (accessToken) in\n                print(\"\\(accessToken ?? \"No token\")\")\n            }\n        }\n    }\n",[255,1266,1267,1277,1294,1311,1334,1338,1342],{"__ignoreMap":257},[282,1268,1269,1272,1274],{"class":284,"line":285},[282,1270,1271],{"class":288},"    func",[282,1273,604],{"class":292},[282,1275,1276],{"class":296},"() {\n",[282,1278,1279,1282,1284,1287,1289,1291],{"class":284,"line":306},[282,1280,1281],{"class":288},"        if",[282,1283,656],{"class":288},[282,1285,1286],{"class":296}," config ",[282,1288,496],{"class":288},[282,1290,539],{"class":315},[282,1292,1293],{"class":296},".config {\n",[282,1295,1296,1299,1302,1304,1306,1309],{"class":284,"line":319},[282,1297,1298],{"class":296},"            TokenService.",[282,1300,1301],{"class":315},"getToken",[282,1303,516],{"class":296},[282,1305,609],{"class":315},[282,1307,1308],{"class":296},": config) { (accessToken) ",[282,1310,1050],{"class":288},[282,1312,1313,1315,1317,1319,1322,1325,1328,1330,1332],{"class":284,"line":330},[282,1314,1083],{"class":315},[282,1316,516],{"class":296},[282,1318,787],{"class":382},[282,1320,1321],{"class":382},"\\(accessToken ",[282,1323,1324],{"class":288},"??",[282,1326,1327],{"class":382}," \"No token\"",[282,1329,1100],{"class":382},[282,1331,787],{"class":382},[282,1333,527],{"class":296},[282,1335,1336],{"class":284,"line":340},[282,1337,1138],{"class":296},[282,1339,1340],{"class":284,"line":347},[282,1341,700],{"class":296},[282,1343,1344],{"class":284,"line":370},[282,1345,415],{"class":296},[211,1347,1348,1349,1352],{},"Finally - for testing - we can add ",[255,1350,1351],{},"self.getToken()"," to the OK clause of askForAuth():",[248,1354,1356],{"className":276,"code":1355,"language":278,"meta":257,"style":257},"            case .OK:\n                self.authenticated = true\n                self.getToken()\n\n",[255,1357,1358,1369,1382],{"__ignoreMap":257},[282,1359,1360,1363,1366],{"class":284,"line":285},[282,1361,1362],{"class":288},"            case",[282,1364,1365],{"class":296}," .OK",[282,1367,1368],{"class":288},":\n",[282,1370,1371,1374,1377,1379],{"class":284,"line":306},[282,1372,1373],{"class":315},"                self",[282,1375,1376],{"class":296},".authenticated ",[282,1378,496],{"class":288},[282,1380,1381],{"class":315}," true\n",[282,1383,1384,1386,1388,1390],{"class":284,"line":319},[282,1385,1373],{"class":315},[282,1387,542],{"class":296},[282,1389,1301],{"class":315},[282,1391,505],{"class":296},[211,1393,1394],{},"This will have to be updated later - but for now it will allow us to trigger the call for testing.",[218,1396],{},[221,1398,1400],{"id":1399},"summary","Summary",[211,1402,1403],{},"So - we now have a token. The next step will be to add support for fetching account info and transaction info.",[218,1405],{},[211,1407,1408],{},[434,1409,1412],{"href":1410,"rel":1411,"target":441},"https:\u002F\u002Fgithub.com\u002Fchrissearle\u002Flommepenger-swiftui",[438,439,440],"GitHub Repository",[1414,1415,1416],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}",{"title":257,"searchDepth":306,"depth":306,"links":1418},[1419,1423,1424],{"id":223,"depth":306,"text":224,"children":1420},[1421,1422],{"id":428,"depth":319,"text":429},{"id":570,"depth":319,"text":571},{"id":1254,"depth":306,"text":1255},{"id":1399,"depth":306,"text":1400},"2020-03-18 09:21 +0100",null,"md","The app so far has the ability to read its configuration and to check that you are who you say you are on devices that support touch or face id. The next step is to get an access token from the S'banken API.",{},"\u002F2020\u002F03\u002F18\u002Ffetching-the-token",{"title":206,"description":213},"Revisiting the Sbanken API with SwiftUI",{"loc":1430},"2020\u002F03\u002F18\u002Ffetching-the-token",[1436,278,1437,1438,1439],"ios","swiftui","xcode","sbanken","uulCaTg84KoDbZSwF4dK-pdinVoo_Yhswl1KgBpAbEo",1775293006983]