davidsansome / tsurukame

Tsurukame is an unofficial WaniKani app for iOS. It helps you learn Japanese Kanji.
https://tsurukame.app
Apache License 2.0
264 stars 63 forks source link

Feature: Syncing recent mistakes #761

Open Deadpikle opened 1 month ago

Deadpikle commented 1 month ago

Recent mistakes is nice, but the last little thing to make it "work well" (while the reviews API is unavailable...) would be to sync recent mistakes to iCloud so that a user could move between devices (e.g. phone, computer) and still do reviews and review recent mistakes.

Ideally, this should be a fairly easy operation:

On user finishing reviews:

  1. Download JSON of recent mistakes (if it exists)
  2. Merge with recent mistakes of just-completed-session, tossing out anything older than 24 hrs as that doesn't matter
  3. Update local DB with recent mistakes (incl. anything from the cloud)
  4. Create new JSON recent mistakes file
  5. Upload to iCloud

On sync operation:

  1. Download JSON of recent mistakes (if it exists)
  2. If it exists, update local DB with recent mistakes from the downloaded file, taking care not to override any newer recent mistakes that are known locally for the same subject

I...have never written iCloud code before. I expected a simple "upload this single file and let me grab it later" operation to be simple, but it was not and/or I did not find a simple wrapper library for iCloud/CloudKit. This library would work well, but it only works for newer versions of iOS/Swift.

So until I have more time, or someone much more knowledgeable than me in this area comes along, here is where I made potential progress:

Save JSON recent mistake to a file

    func mistakesUrl() -> URL {
      let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
      return URL(fileURLWithPath: "\(paths[0])/mistakes.json")
    }

    var jsonData: Data!
    do {
      var mistakes = getRecentMistakeTimes() // defined in LocalCachingClient.swift
      var arrForJson = [String: String]()
      mistakes.forEach { item in
        arrForJson[String(item.key)] = item.value // must have string keys otherwise JSONSerialization complains
      }
      jsonData = try JSONSerialization.data(withJSONObject: arrForJson,
                                            options: JSONSerialization.WritingOptions())
      let jsonString = String(data: jsonData as Data,
                              encoding: String.Encoding(rawValue: NSUTF8StringEncoding)) // debug
      print(jsonString) // debug
    } catch let error as NSError {
      print("Array to JSON conversion failed: \(error.localizedDescription)")
    }
    let jsonFilePath = mistakesUrl()
    do {
      try jsonData.write(to: jsonFilePath)
      print("JSON data was written to the file successfully!")
    } catch let error as NSError {
      print("Couldn't write to file: \(error.localizedDescription)")
    }

I had some attempts at saving and loading records from iCloud based on CKRecord/CKAsset, but that doesn't seem like the thing to do for this sort of operation. This SO post or this one may help with actually reading/writing the file.

In addition, for this to work fully, the app creator for Tsurukame may need to create an official iCloud container for this.