Apple Notes in iOS 18

 · 12 mins read

TL;DR: iOS 18 brought far less changes to Apple Notes than many recent releases which is a good thing because I am way too delinquent on these posts.

Three Years?

While not directly related to the subject at hand, I feel it necessary first to explain a nearly three year gap in posts on this subject.

These past three years were exceptionally hard from a personal perspective, starting with a family emergency that took me away from active development and research for about eight months. I am very grateful that everything worked together for good, but that crucible put me in such a hole that just getting the Apple Cloud Notes Parser back under development and caught up with current iOS features was a daunting task.

I had hoped with the release of iOS 17 last year to get back into very active research, but similar to iOS 15, iOS 17 brought significant changes from a forensics perspective. I never felt I had done enough to explain the changes and there are still aspects where I’m not completely satisfied with my solution, so I never published that post.

Between the changes in iOS 17 and a growing feature list in Apple Cloud Notes Parser to support different user requirements, I found it hard to jump in and make quick changes without worrying something else would break. This led me to taking massive detour to adding RSpec tests into the program, figuring out how to generate and include clean-enough data that others could run the same tests without having my personal device information in the repo, and containerizing the program to make life easier for others (and for automated testing). Ultimately, this put the program in a better situation because its features have expanded beyond what I had originally envisioned. However, it took a lot of time while I was still dealing with other issues, so it was a lot slower than I wanted.

The last part of this delay was the fact that I built the test infrastructure for Apple Cloud Notes Parser and my lab seven years ago. The Mac and iPhones I used to generate data all aged off of current updates during the past three years, leading me to have to invest a good bit of time and money in rebuilding. To this end, I was extremely grateful for the GitHub sponsors who regularly contribute for the project as it helped defray the cost of that new equipment.

With that, let’s look at iOS 18.

Structural Changes

Between iOS 17.6 and 18.0, Apple added the following 7 columns to the ZICCLOUDSYNCINGOBJECT table, expanding that table yet again to a total of 209 columns! The last column (ZUNAPPLIEDENCRYPTEDRECORDDATA) is actually a renamed version of ZUNAPPLIEDENCRYPTEDRECORD, which is now dropped from the table. This post will cover how those columns are used in Apple’s addition of four new features to Apple Notes.

ZDIDRUNPAPERFORMDETECTION
ZHASEMPHASIS
ZHASPAPERFORM
ZHOSTAPPLICATIONIDENTIFIER
ZNEEDSTRANSCRIPTION
ZOUTLINESTATEDATA
ZUNAPPLIEDENCRYPTEDRECORDDATA

Changelog

Apple’s changelog for iOS 18 lists the following updates for Notes and these categories are the primary way we will look at the new columns:

  • Audio recordings in Notes are automatically transcribed live
  • Notes now support interactive math
  • Headings are now collapsible
  • Text can be highlighted in five different colors for emphasis

Audio Recordings

Having audio recordings inside Apple Notes as a first-class embedded object is cool, but not a huge departure from the norm. In fact, I accidentally discovered that Apple Cloud Notes Parser supported it even before I made any changes. Given the past few years, that was a very welcome discovery.

As was the case when attaching a recording in previous versions of iOS, a new com.apple.m4a-audio object is created in the note when you use the new dedicated record audio button.
The biggest differences for recordings in iOS 18 is that the recording is transcribed. The transcript appears in the ZADDITIONALINDEXABLETEXT column for the same com.apple.m4a-audio object. As of launch, transcription is limited to a few specific English variants, but presumably that will expand in the future. If the recording is renamed from its original “New Recording.m4a”, the user-provided filename will appear in the ZICCLOUDSYNCINGOBJECT.ZUSERTITLE column. Note that audio recordings after iOS 18 appear to have a ZFILESIZE of 0 which was the main difference I saw other than the transcript.

You can find all the embedded recordings (including prior to iOS 18) and their transcripts using the following SQLite query.

SELECT ZNOTE as Note, 
  ZADDITIONALINDEXABLETEXT as Transcript, 
  ZTITLE as Title, 
  ZNEEDSTRANSCRIPTION as Needs,
  ZFILESIZE as Size
FROM ZICCLOUDSYNCINGOBJECT 
WHERE ZTYPEUTI='com.apple.m4a-audio'
ORDER BY ZNOTE;
Note Transcript Title Need Size
246 NULL New Recording 3.m4a NULL 43763
590 Recording audio, recording audio, testing. First Recording 0 0
590 This audio I will not click the transcript button on. Second Recording NULL 0

Interactive Math

This neat little feature also was already supported by Apple Cloud Notes Parser. It adds two new com.apple.notes.inlinetextattachment types, one for the results of calculations and one for graph equations, but most of its functionality behaves very similarly to the existing inline attachments.

Similar to hashtags and mentions, these two new types are embedded objects that have the text they display kept in the ZALTTEXT field. The text is UTF-8 encoded and uses white space characters from the U+200x series, so display may or may not fit nicely next to your plain old ASCII space. Only the part that is being calculated is stored in the new type.

For example, a simple (com.apple.notes.inlinetextattachment.calculateresult) equation of 2 + 3 = 5 would have the = 5 as the object that is stored. A hexdump of what is in ALTTEXT looks like:

00000000  e2 80 8e e2 80 89 3d e2 80 89 35                 |......=...5|

In UTF-8, that breaks out to being characters:

  • LEFT-TO-RIGHT MARK
  • THIN SPACE
  • ’=’
  • THIN SPACE
  • ‘5’

Because THIN SPACE is being used, text alignment with “normal” spaces will look a little off, but presumably it opens up options to represent a lot more math symbols correctly.

Graphed equations (com.apple.notes.inlinetextattachment.calculategraphexpression) are a bit more involved. In that case, they store the left hand side of the equation (the variable being calculated) and generate an additional com.apple.paper attachment displaying the plot. In the note itself, it will show an animation of the graph occurring!

For example, a note with the following text…

y= sin(x)
y= cos(x)

… would generate this graph that plots both of those values:

Graph of Sin and Cosine

Collapsible Headings

This appears to be an entirely cosmetic feature, with any “Heading” text now having the ability to toggle its contents between displayed and hidden. That said, it did add the ZICCLOUDSYNCINGOBJECT.ZOUTLINESTATEDATA column. This column contains a protobuf that appears to record which headings are collapsed, with the first field containing a last updated timestamp, given in Cocoa Core Data timestamp format and the second field containing an object. That object’s seventh field is a string which converts into an array of the collapsed header UUIDs. If a note has had any headers collapsed ever, but has none currently collapsed, the string is just an empty array.

If you use Protobuf Inspector, this config…

types = {
  "root": {
    1: ("double", "Timestamp"),
    2: ("collapsed_headers"),
  },

  "collapsed_headers": { 
    7: ("string", "UUID Array")
  },
}

… would produce this output if run on the raw value found in ZICCLOUDSYNCINGOBJECT.ZOUTLINESTATEDATA

root:
    1 Timestamp = 7.4822701e+08
    2 <collapsed_headers> = collapsed_headers:
        7 UUID Array = "["BF79CA29-3E62-4134-9A1D-31F9A98282BE"]"

… for a rather simple note consisting of just two headings with the first one collapsed …

[notta@cuppa ~]$ python ~/protobuf-inspector/main.py < ~/outline.bin \
    | grep "1: Heading" -B 3 -A 3 \
    | grep "Attribute Run\|Style Type\|0000"
            5 Attribute Run = attribute_run:
                    1 Style Type = 1: Heading
                        0000   BF 79 CA 29 3E 62 41 34 9A 1D 31 F9 A9 82 82 BE
            5 Attribute Run = attribute_run:
                    1 Style Type = 1: Heading
                        0000   71 0A 0C DD 40 23 41 9D 83 39 99 7D A0 E0 7A 0E

Text Emphasis

Like collapsible headings, text emphasis is a very cosmetic feature and allows the user to highlight text in one of a few different colors. It shows up in the database similar to other textual formatting as field 14 of the attribute run. This field holds an integer, of which the currently valid values are:

  1. Purple
  2. Pink
  3. Orange
  4. Mint
  5. Blue

As an example of where these fields appear, the below (slightly snipped for readability) excerpt highlights its position if Protobuf Inspector was executed from a folder containing this config on the gunzipped protobuf of a note:

[notta@cuppa ~]$ python ~/protobuf-inspector/main.py < ~/emphasis_colors.bin
root:
    1 <varint> = 0
    2 <document> = document:
        1 <varint> = 0
        2 Version = 0
        3 Note = note:
            2 Note Text = "Text highlighting\n\nPurple-Pink-Orange-Mint-Blue\n\n"
            5 Attribute Run = attribute_run:
                1 Length = 19
                2 Paragraph Style = paragraph_style:
                    1 Style Type = 0: Title
            5 Attribute Run = attribute_run:
                1 Length = 7
                14 Emphasis Color = 1: Purple
            5 Attribute Run = attribute_run:
                1 Length = 5
                14 Emphasis Color = 2: Pink
            5 Attribute Run = attribute_run:
                1 Length = 7
                14 Emphasis Color = 3: Orange
            5 Attribute Run = attribute_run:
                1 Length = 5
                14 Emphasis Color = 4: Mint
            5 Attribute Run = attribute_run:
                1 Length = 4
                14 Emphasis Color = 5: Blue

In the context of iOS 18’s new fields, you can find all notes that use textual emphasis colors using this SQL query:

SELECT Z_PK,
       ZCLOUDSTATE as NoteID,
       ZTITLE1 as Title 
FROM ZICCLOUDSYNCINGOBJECT 
WHERE ZHASEMPHASIS IS TRUE 
ORDER BY Z_PK;
Z_PK NoteID Title
586 580 Complicated emphasis (Mint)
596 592 Text Highlighting

Conclusion

While the changes in iOS 18 will do well to show off Apple’s look and feel improvements in Apple Notes, thankfully they don’t reflect any signficant changes from a forensic perspective. To conclude, the below table summarizes how each of the new columns in iOS 18’s ZICCLOUDSYNCINGOBJECT table play into those new features:

Column New? Use
ZDIDRUNPAPERFORMDETECTION Yes Unsure, possibly Interactive Math
ZHASEMPHASIS Yes Text emphasis
ZHASPAPERFORM Yes Interactive Math
ZHOSTAPPLICATIONIDENTIFIER Yes Unsure, but possibly related to iOS 18.1 allowing transcripts from the phone app
ZNEEDSTRANSCRIPTION Yes Audio recording
ZOUTLINESTATEDATA Yes Collapsible Headers
ZUNAPPLIEDENCRYPTEDRECORDDATA Renamed Where the server’s encrypted data does not yet match the local data