Home > Software engineering >  Data compressed in Swift 5 on macOS/iPhone cannot be decompressed by anything else
Data compressed in Swift 5 on macOS/iPhone cannot be decompressed by anything else

Time:02-16

I am trying to decompress some JSON (representing a Codable type) that was compressed on an iPhone, using the NSData instance method .compressed(using: .zlib). Note .zlib is the format recommended by Apple for portability.

The output bytes are not recognized by tools like Python 3's zlib library (on macOS 12.2 or Ubuntu Linux 21.10) or the command-line uncompress (on macOS).

Question: how can I decompress this data on other devices/not in Swift on Apple?

Here's a basic example which can be thrown into compressor.swift and compiled/run to produce the compressed data, following this guide

import Foundation

struct SomeThing: Codable {
  var thing: String
  
  init(thing: String) {
    self.thing = thing
  }
}

let thing = SomeThing(thing: "abc")

let encoder = JSONEncoder()

let encodedThing = try encoder.encode(thing)

let compressedEncodedThing = try (encodedThing as NSData).compressed(using: .zlib)

try compressedEncodedThing.write(to: URL(fileURLWithPath: "/path/to/somewhere/abc.Z"),
                                 options: [])

Compiling and running that on macOS produces abc.Z as expected.

This code will decompress the file we just wrote, so long as we compile on macOS:

import Foundation

let readBytes = try! Data(contentsOf: URL(fileURLWithPath: "/path/to/somewhere/abc.Z"))

let decompressed = try! (readBytes as NSData).decompressed(using: .zlib) as Data

let decompString = String(decoding: decompressed, as: UTF8.self)

print(decompString) // prints {"thing": "abc"}

Trying the same thing on Linux (Ubuntu 21.10 with Swift 5.5.3 Release toolchain),

$ swiftc decompressor.swift 
decompressor.swift:5:47: error: value of type 'NSData' has no member 'decompressed'
let decompressed = try! (readBytes as NSData).decompressed(using: .zlib) as Data
                        ~~~~~~~~~~~~~~~~~~~~~ ^~~~~~~~~~~~
decompressor.swift:5:68: error: cannot infer contextual base in reference to member 'zlib'
let decompressed = try! (readBytes as NSData).decompressed(using: .zlib) as Data

which suggests that the open sourced NSData lacks some methods it has on Apple hardware/OS.

Other things I've tried that strongly suggest true ZLib encoding is not being used:

  • Calling
    uncompress abc.Z
    
    (from the same macOS computer that produced abc.Z) returns
    uncompress: abc.Z: Inappropriate file type or format
    
    I've also tried using gunzip, to similar failure message.
  • Tried zlib.decompress in Python 3. The Python failure is perhaps the most informative: this script crashes with error error: Error -3 while decompressing data: incorrect header check
    import zlib
    
    with open("/path/to/somewhere/abc.Z", "rb") as f:
        bytesRead = f.read()
    
    decompressed = zlib.decompress(bytesRead)
    
    print(decompressed)
    
  • The first 20 bytes of the file, in hexadecimal, are:
    $ od -x -N 20 abc.Z
    0000000  56ab  c92a    ccc8    574b    52b2    4c4a    564a    05aa
    0000020  0000                                                        
    0000021
    

CodePudding user response:

Your data appears to be a raw deflate stream. In python you can try zlib.decompress(bytesRead, wbits=-15).

  • Related