0

I have a navigation and location tracking app. While a user is tracking his trip, coordinates, speed, timestamp (and a few more) are logged with each coordinate that comes in. I don't want to store this in memory as that would make the app memory grow as the user moves along, eventually leading to a didReceiveMemoryWarning and even an app crash. (At least that's been my experience so far)

What would be the most efficient way to do this? Thinking of battery consumption, CPU usage and also memory usage if that comes into play.

I can think of two options to do this:

  1. Log in a file (what I'm currently doing, using this code snipped):
let newLine: String = "bla bla bla"
let url: URL = URL(string: "someLocalPath")
newLine.appendToURL(url)

extension String {
    func appendToURL(_ fileURL: URL) throws {
        let data = self.data(using: String.Encoding.utf8)!
        try data.appendToURL(fileURL)
    }
}

extension Data {
    func appendToURL(_ fileURL: URL) throws {
        if let fileHandle = try? FileHandle(forWritingTo: fileURL) {
            defer {
                fileHandle.closeFile()
            }
            fileHandle.seekToEndOfFile()
            fileHandle.write(self)
            fileHandle.closeFile()
        }
        else {
            try write(to: fileURL, options: .atomic)
        }
    }
}
  1. Using Core Data

    I have not tried this yet, but it would be relatively easy I believe. Just creating an entity with all the fields required, and then adding a managedObject for each received coordinate.

So, to repeat my question: which of these two options would be more efficient from a battery, CPU and memory perspective?

Additional question: Is there perhaps another way to do this. One that I didn't think of?

Marwen Doukh
  • 1,946
  • 17
  • 26
guido
  • 2,792
  • 1
  • 21
  • 40
  • Well, either of them is going to hit the disk. The only way to know what is most cost-effective is measure measure measure. This is why we have Instruments. There is some good discussion in https://developer.apple.com/videos/play/wwdc2019/419/ – matt Jul 10 '19 at 17:33

1 Answers1

0

I found another option that works best in my case. Should have thought of that in the first place ‍♂️.

So, I did not want to log to memory because it could make iOS kill my app due to excessive memory usage. But, iOS sends a didReceiveMemoryWarning first, upon which you can reduce the memory footprint of the app, preventing it from being killed.

So, what I do now:

I added a instance variable:

fileprivate var logString: String = ""

And log to it when a new coordinate is received:

logString += newLine

If / when I get a didReceiveMemoryWarning I write the entire logString to the file on disk and empty it afterwards:

override func didReceiveMemoryWarning() {
    logString.appendToURL(url)
    logString = ""
}

This way I only write to disk when necessary (and not with each coordinate received), which is a lot more energy efficient. (Writing to disk costs more energy than writing to memory. Source: https://developer.apple.com/videos/play/wwdc2016/719/)

Plus, my app is prevented from being killed by iOS due to high memory usage, because I clear up memory on time.

guido
  • 2,792
  • 1
  • 21
  • 40