Skip to content

Commit

Permalink
feat: implement last.fm API signature
Browse files Browse the repository at this point in the history
  • Loading branch information
angristan committed Jul 17, 2021
1 parent 6810bca commit c1fa1b6
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 3 deletions.
10 changes: 10 additions & 0 deletions firstfm.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
82BBD2462698B13B009B42FC /* Country.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82BBD2452698B13B009B42FC /* Country.swift */; };
82BBD2482698B37F009B42FC /* TrendsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82BBD2472698B37F009B42FC /* TrendsViewModel.swift */; };
82BBD24A2698C33D009B42FC /* TopCountryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82BBD2492698C33D009B42FC /* TopCountryView.swift */; };
82C282E026A2E474000E5F41 /* LoginResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82C282DF26A2E474000E5F41 /* LoginResponse.swift */; };
82C282E226A31CFD000E5F41 /* StringMD5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82C282E126A31CFD000E5F41 /* StringMD5.swift */; };
82D5B4D02696DB8500716931 /* ScrobblesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82D5B4CF2696DB8400716931 /* ScrobblesView.swift */; };
82D5B4D22696DBD700716931 /* ScrobblesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82D5B4D12696DBD700716931 /* ScrobblesViewModel.swift */; };
82D5B4D42696DC7800716931 /* RecentTracks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82D5B4D32696DC7800716931 /* RecentTracks.swift */; };
Expand Down Expand Up @@ -89,6 +91,8 @@
82BBD2452698B13B009B42FC /* Country.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Country.swift; sourceTree = "<group>"; };
82BBD2472698B37F009B42FC /* TrendsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendsViewModel.swift; sourceTree = "<group>"; };
82BBD2492698C33D009B42FC /* TopCountryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopCountryView.swift; sourceTree = "<group>"; };
82C282DF26A2E474000E5F41 /* LoginResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginResponse.swift; sourceTree = "<group>"; };
82C282E126A31CFD000E5F41 /* StringMD5.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringMD5.swift; sourceTree = "<group>"; };
82D5B4CF2696DB8400716931 /* ScrobblesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrobblesView.swift; sourceTree = "<group>"; };
82D5B4D12696DBD700716931 /* ScrobblesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrobblesViewModel.swift; sourceTree = "<group>"; };
82D5B4D32696DC7800716931 /* RecentTracks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentTracks.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -119,6 +123,7 @@
isa = PBXGroup;
children = (
4A8C296026983B7300F55ECC /* LastFMAPI.swift */,
82C282E126A31CFD000E5F41 /* StringMD5.swift */,
);
path = Utils;
sourceTree = "<group>";
Expand Down Expand Up @@ -225,6 +230,7 @@
82D5B4D32696DC7800716931 /* RecentTracks.swift */,
82F0D54E2697AC66007CEA98 /* ArtistSearch.swift */,
8226AB2326998FAB007ECE5F /* TopCountryArtists.swift */,
82C282DF26A2E474000E5F41 /* LoginResponse.swift */,
);
path = API;
sourceTree = "<group>";
Expand Down Expand Up @@ -348,6 +354,7 @@
82505CE02675074E00CCCB58 /* ChartListView.swift in Sources */,
82D5B4D42696DC7800716931 /* RecentTracks.swift in Sources */,
82A006C1267963BB0009BD71 /* SpotifyImage.swift in Sources */,
82C282E026A2E474000E5F41 /* LoginResponse.swift in Sources */,
82FBAE472674B8FC000D8E29 /* Artist.swift in Sources */,
82D5B4D02696DB8500716931 /* ScrobblesView.swift in Sources */,
82505CDF2675074E00CCCB58 /* ArtistRow.swift in Sources */,
Expand All @@ -365,6 +372,7 @@
82F0D54F2697AC66007CEA98 /* ArtistSearch.swift in Sources */,
82A006BE2679636F0009BD71 /* TrackSearchResponse.swift in Sources */,
82BBD2462698B13B009B42FC /* Country.swift in Sources */,
82C282E226A31CFD000E5F41 /* StringMD5.swift in Sources */,
820455E0267ABC930009A418 /* AuthViewModel.swift in Sources */,
4A8C296126983B7300F55ECC /* LastFMAPI.swift in Sources */,
82BBD24A2698C33D009B42FC /* TopCountryView.swift in Sources */,
Expand Down Expand Up @@ -511,6 +519,7 @@
INFOPLIST_FILE = firstfm/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LAST_FM_KEY = d404c94c63e190519d70002332f09509;
LAST_FM_SHARED_SECRET = e4e1eb5bf14d2418f51ed6ea6ae5d91a;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand All @@ -535,6 +544,7 @@
INFOPLIST_FILE = firstfm/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LAST_FM_KEY = d404c94c63e190519d70002332f09509;
LAST_FM_SHARED_SECRET = e4e1eb5bf14d2418f51ed6ea6ae5d91a;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down
30 changes: 27 additions & 3 deletions firstfm/Data/Utils/LastFMAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,41 @@ import Foundation
class LastFMAPI {
// swiftlint:disable force_cast
static let lastFMAPIKey = Bundle.main.object(forInfoDictionaryKey: "LastFMAPIKey") as! String
// swiftlint:disable force_cast
static let lastFMSharedSecret = Bundle.main.object(forInfoDictionaryKey: "LastFMSharedSecret") as! String

static func request<T: Decodable>(method: String = "POST", lastFMMethod: String, args: [String : String] = [:], callback: @escaping (_ Data: T?, Error?) -> Void) -> Void {
var request = URLRequest(url: URL(string: "https://ws.audioscrobbler.com/2.0/?format=json")!)

// We need to add these into the dict to compute the signature
var fullArgs = args
fullArgs["method"] = lastFMMethod
fullArgs["api_key"] = lastFMAPIKey

var argsString = ""

for (key, value) in args {
argsString += "&\(key)=\(value)"
// Final format will be md5([param1][value1]..[paramN][valueN][shared_secret])
// https://www.last.fm/api/mobileauth
var toSign = ""

for key in Array(fullArgs.keys).sorted(by:<) {
if argsString != "" {
// Not first param
argsString += "&"
}
argsString += "\(key)=\(fullArgs[key] ?? "")"

toSign += key
toSign += fullArgs[key] ?? ""
}

let data : Data = "api_key=\(lastFMAPIKey)&method=\(lastFMMethod)\(argsString)".data(using: .utf8)!
toSign += lastFMSharedSecret

let data : Data = "\(argsString)&api_sig=\(toSign.md5Value)".data(using: .utf8)!

print("argsString: \(argsString)")
print("toSign: \(toSign)")
print("api_sig: \(toSign.md5Value)")

request.httpMethod = method
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField:"Content-Type");
Expand Down
31 changes: 31 additions & 0 deletions firstfm/Data/Utils/StringMD5.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// StringMD5.swift
// firstfm
//
// Created by Stanislas Lange on 17/07/2021.
//

import Foundation
import CommonCrypto

// Needed to compute LastFM api Signature
// https://www.last.fm/api/mobileauth

extension String {
var md5Value: String {
let length = Int(CC_MD5_DIGEST_LENGTH)
var digest = [UInt8](repeating: 0, count: length)

if let d = self.data(using: .utf8) {
_ = d.withUnsafeBytes { body -> String in
CC_MD5(body.baseAddress, CC_LONG(d.count), &digest)

return ""
}
}

return (0 ..< length).reduce("") {
$0 + String(format: "%02x", digest[$1])
}
}
}
2 changes: 2 additions & 0 deletions firstfm/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,7 @@
<string>$(LAST_FM_KEY)</string>
<key>SpotifyAPIToken</key>
<string>$(SPOTIFY_API_TOKEN)</string>
<key>LastFMSharedSecret</key>
<string>$(LAST_FM_SHARED_SECRET)</string>
</dict>
</plist>

0 comments on commit c1fa1b6

Please sign in to comment.