4

I'd like to be able to inflate/deflate Swift3 Data structs. I found GzipSwift, but it's not clear how I make that available to my iOS app. The naive things I've tried include:

  1. Copying the Data+Gzip.swift file into my own project. This then complains about the import zlib at the top of said file. I think that has something to do the with the modulemap files in the zlib directory of the same sources. But I'm not sure what or how to recreate those in my own project.

  2. Cloned the repository from github, opened XCode and built in (pressed the run button basically). Then tried to add that as a linked library or framework to my own project. I'm pretty sure just selecting the top level directory of the repository is not what I want to do, but I didn't know what else to try.

I've found some other code out there, but it seems dated and relative to Swift2.

Travis Griggs
  • 18,930
  • 17
  • 76
  • 137

3 Answers3

14

I just recently had to add that exact library and file to my project, and after a lot of troubleshooting finally got it working, so let me walk you through the steps!

Okay

1) Go to the top level directory of your project in finder, and create a new folder called Swiftzlib or whatever you want the name of the module that you will be importing to be. (What we will do is add the zlib library as a module, so think of it as importing Foundation or some such other module). To clarify, this Swiftzlib directory will end up as a child directory of the same directory that contains your *.xcodeproj and *.xcworkspace files.

2) Inside the folder you created, make two files.

  • include.h
  • module.modulemap

3) In your include.h file, enter the following:

#include<zlib.h>

4) In your module.modulemap file, enter the following:

module Swiftzlib [system] {
    header "include.h"
    export *
}

Where Swiftzlib is the same as the name of the folder that you created.

5) Open your Xcode project, and select your target

  • 5a) In Build Phases -> Link Binary with Libraries, add libz.tbd
  • 5b) In Build Settings -> Swift Compiler - Search Paths, add $(PROJECT_DIR)/Swiftzlib non-recursively to the import paths
  • 5c) In Build Settings -> Other Linker Flags, add -lz as a flag

6) Select your project in Xcode (may not be necessary, but I've done it in my project and it works)

  • 6a) In Build Settings -> Swift Compiler - Search Paths, add $(PROJECT_DIR)/Swiftzlib non-recursively to the import paths

7) In Data+Gzip.swfit, add import Swiftzlib to the top of the file

8) Clean, Build, and Run!

Travis Griggs
  • 18,930
  • 17
  • 76
  • 137
pbush25
  • 5,048
  • 2
  • 23
  • 33
  • Wow! Thanks for the detailed reply. I'll give it a try tomorrow and hopefully accept the answer then. – Travis Griggs Sep 23 '16 at 00:15
  • Sadly, I still get the "No such module Swiftzlib" error :( – Travis Griggs Sep 23 '16 at 16:37
  • Make sure that the path pointing to the library is correct, this is crucial. – pbush25 Sep 23 '16 at 16:38
  • Got it! My misunderstanding was in what constitutes the "top level directory of your project". In fact, maybe a lightbulb is coming on here... XCode tends to put all of the .swift sources in a project in a directory of the same name as the directory above it. Is this because that represents the intrinsic module for the project? – Travis Griggs Sep 23 '16 at 16:48
  • You could have placed the module anywhere within your projects file structure, you just would have had to choose the right path in the import section. As for your second question... I'm not sure. Xcode does this, even for obj-c projects so it could just be the file structure. I'm pretty sure with swift if files are in the same namespace (as in your project name) they can all be seen by the compiler without having to import them. You never import AppName as a module in your application. – pbush25 Sep 23 '16 at 16:51
  • i have followed all your steps but no luck...please guide me...in me Data_Gzip.swift it can't find Error and Data class – H Raval Mar 03 '17 at 10:33
  • @HRaval that sounds like you're importing the Data+Gzip.swift class as the Swift 3 version in a non Swift 3 project – pbush25 Mar 03 '17 at 14:27
  • i am using swift 2.3 – H Raval Mar 06 '17 at 05:23
  • @HRaval right do you need to get the Swift 2.3 version of NSData+gzip instead of Data+Gzip – pbush25 Mar 06 '17 at 12:47
  • Thanx a lot.may you please provide me a link? – H Raval Mar 06 '17 at 13:15
  • @HRaval https://github.com/1024jp/GzipSwift/releases/tag/2.0.0 here is the link to an older release – pbush25 Mar 07 '17 at 16:41
  • that is very useful! – landerlyoung Jul 24 '17 at 08:37
8

Swift 5 implementation using Compression.

Took me a few days to realise I had to drop the first 2 bytes of the compressed data. Hope it can help somebody else.

import Foundation
import Compression

func decompress(_ data: Data) -> String {
    let size = 8_000_000
    let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: size)
    let result = data.subdata(in: 2 ..< data.count).withUnsafeBytes ({
        let read = compression_decode_buffer(buffer, size, $0.baseAddress!.bindMemory(to: UInt8.self, capacity: 1),
                                             data.count - 2, nil, COMPRESSION_ZLIB)
        return String(decoding: Data(bytes: buffer, count:read), as: UTF8.self)
    }) as String
    buffer.deallocate()
    return result
}
vauxhall
  • 1,761
  • 16
  • 14
  • 1
    @The_Androctonus `size` is the allocating size for the buffer, that is, the amount of bytes reserved to be used. Usually a big number will do if you don't know how many bytes to expect when uncompressed. – vauxhall Jun 15 '19 at 18:32
  • It took me few days finding this same thing... Thanks for this answer. – Cinn Jun 20 '19 at 10:05
  • i would have pulled my hair out figuring this one out! Thanks @vauxhall! – Rafthecalf Jun 11 '20 at 15:46
  • you saved my life! After days I finally come across this answer. And I'm wondering why? – Roger Sep 02 '20 at 03:55
3

I maintain a small Swift 3+ wrapper around Apples native libcompression framework at:

https://github.com/mw99/DataCompression

Usage example for gzip:

let data: Data! = "https://www.ietf.org/rfc/rfc1952.txt".data(using: .utf8)
let gzipped: Data! = data.zip()
let gunzipped: Data? = gzipped.unzip()
assert(data == gunzipped)

But if you are only interested in classic inflate and deflate you may use the .inflate() and .deflate() methods instead. That will save 18 bytes because the gzip header won't be added.

LimeRed
  • 998
  • 10
  • 15