CSV reading and writing library written in Swift.

Overview

CSV.swift

Build Status codecov Open Source Helpers

CSV reading and writing library written in Swift.

Usage for reading CSV

From string

import CSV

let csvString = "1,foo\n2,bar"
let csv = try! CSVReader(string: csvString)
while let row = csv.next() {
    print("\(row)")
}
// => ["1", "foo"]
// => ["2", "bar"]

From file

NOTE: The default character encoding is UTF8.

import Foundation
import CSV

let stream = InputStream(fileAtPath: "/path/to/file.csv")!
let csv = try! CSVReader(stream: stream)
while let row = csv.next() {
    print("\(row)")
}

Getting the header row

import CSV

let csvString = "id,name\n1,foo\n2,bar"
let csv = try! CSVReader(string: csvString,
                         hasHeaderRow: true) // It must be true.

let headerRow = csv.headerRow!
print("\(headerRow)") // => ["id", "name"]

while let row = csv.next() {
    print("\(row)")
}
// => ["1", "foo"]
// => ["2", "bar"]

Get the field value using subscript

import CSV

let csvString = "id,name\n1,foo"
let csv = try! CSVReader(string: csvString,
                         hasHeaderRow: true) // It must be true.

while csv.next() != nil {
    print("\(csv["id"]!)")   // => "1"
    print("\(csv["name"]!)") // => "foo"
}

Provide the character encoding

If you use a file path, you can provide the character encoding to initializer.

import Foundation
import CSV

let stream = InputStream(fileAtPath: "/path/to/file.csv")!
let csv = try! CSVReader(stream: stream,
                         codecType: UTF16.self,
                         endian: .big)

Reading a row into a Decodable object

If you have a destination object that conforms to the Decodable protocol, you can serialize a row with a new instances of the object.

struct DecodableExample: Decodable {
    let intKey: Int
    let stringKey: String
    let optionalStringKey: String?
}

let csv = """
    intKey,stringKey,optionalStringKey
    1234,abcd,
    """

var records = [DecodableExample]()
do {
    let reader = try CSVReader(string: csv, hasHeaderRow: true)
    let decoder = CSVRowDecoder()
    while reader.next() != nil {
        let row = try decoder.decode(DecodableExample.self, from: reader)
        records.append(row)
    }
} catch {
    // Invalid row format
}

Usage for writing CSV

Write to memory and get a CSV String

NOTE: The default character encoding is UTF8.

import Foundation
import CSV

let csv = try! CSVWriter(stream: .toMemory())

// Write a row
try! csv.write(row: ["id", "name"])

// Write fields separately
csv.beginNewRow()
try! csv.write(field: "1")
try! csv.write(field: "foo")
csv.beginNewRow()
try! csv.write(field: "2")
try! csv.write(field: "bar")

csv.stream.close()

// Get a String
let csvData = csv.stream.property(forKey: .dataWrittenToMemoryStreamKey) as! Data
let csvString = String(data: csvData, encoding: .utf8)!
print(csvString)
// => "id,name\n1,foo\n2,bar"

Write to file

NOTE: The default character encoding is UTF8.

import Foundation
import CSV

let stream = OutputStream(toFileAtPath: "/path/to/file.csv", append: false)!
let csv = try! CSVWriter(stream: stream)

try! csv.write(row: ["id", "name"])
try! csv.write(row: ["1", "foo"])
try! csv.write(row: ["1", "bar"])

csv.stream.close()

Installation

CocoaPods

pod 'CSV.swift', '~> 2.4.3'

Carthage

github "yaslab/CSV.swift" ~> 2.4.3

Swift Package Manager

.package(url: "https://github.com/yaslab/CSV.swift.git", .upToNextMinor(from: "2.4.3"))

Reference specification

License

CSV.swift is released under the MIT license. See the LICENSE file for more info.

Comments
  • Strange output?

    Strange output?

    For the most part this library works great! But I'm encountering some strange things when iterating over a parsed CSV in my unit tests. Sometimes the parsing seems to fail and the values are populated with goggly goop like this column value of \0\0\0è•ä\u{05}\0\0\0\0\0\0\0\0\0\0\0\0\0…+\u{07}\u{01}\0\0\0Ø.

    Not sure if anyone else has encounter this, I haven't been able to figure out any sort of patter as to why it is happening.

    bug 
    opened by kgn 4
  • Doesn't escape strings with newlines.

    Doesn't escape strings with newlines.

    I would love to submit a fix, @yaslab please let me know if I can go ahead.

    Also-- I see you have some automated tests. I've never run automated tests in Xcode, but I was thinking I can figure it out and add a contributing.md file for this repo to document how to run and add tests for future features or bugfixes. Might make it easier for others to open up good pull requests and share the workload.

    opened by wsaglime 3
  • decoding causes memory leak

    decoding causes memory leak

    This code causes a memory leak.

    struct DecodableExample: Decodable {
        let dateKey: Date
        let stringKey: String
        let optionalStringKey: String?
    }
    
    
    for i in 1...20 {
        var records = [DecodableExample]()
        var ii = 0
        do {
            let csv = try String(contentsOfFile: "/tmp/test.txt", encoding: .utf8)
            let reader = try CSVReader(string: csv, hasHeaderRow: true, trimFields: true, delimiter: ";", whitespaces: .whitespaces)
            let decoder = CSVRowDecoder()
            let formatter = DateFormatter()
            formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
            decoder.dateDecodingStrategy = .formatted(formatter)
            while reader.next() != nil {
                ii += 1
                let row = try decoder.decode(DecodableExample.self, from: reader)
                records.append(row)
            }
        } catch {
            print("\(ii)-\(error)")
        }
        print(i)
    }
    

    file : test.txt

    memory graph: image

    opened by a72 3
  • pod 'CSV.swift', '~> 2.2.1'

    pod 'CSV.swift', '~> 2.2.1'

    causes

    [!] CocoaPods could not find compatible versions for pod "CSV.swift": In Podfile: CSV.swift (~> 2.2.1)

    None of your spec sources contain a spec satisfying the dependency: CSV.swift (~> 2.2.1).

    question 
    opened by dadidu67 3
  • Possibility to specify characters to ignore while reading the file

    Possibility to specify characters to ignore while reading the file

    Sometimes, CSV files are formatted in a way that they stay well human readable, for example with a delimiter character followed by a tabstop. Would it be possible to integrate a functionality to ignore, for example, all \t in the input stream? I know that I can preprocess my CSV files to achieve the same, but this cannot always be ensured for other users of the software.

    enhancement 
    opened by florianheller 3
  • memory leaks

    memory leaks

    This code causes a memory leak. If I use JSON decoder then there is no memory leak. Any idea how to fix this?

    import Foundation
    import CSV
    
    typealias  TestCodable = Test & Codable
    
    protocol Test {
        func baz()
    }
    
    struct Product1:TestCodable {
        var title:String
        var price:Int
        var quantity:Int
        func baz() {
            print(title)
        }
    }
    
    let csv = """
    title,price,quantity
    Nike,10,1
    Nike1,11,1
    Nike2,12,1
    Nike3,13,1
    Nike4,13,1
    Nike5,13,1
    Nike6,13,1
    """
    
    let json = """
    {
    "title": "Nike",
    "price": 10,
    "quantity": 1
    }
    """
    let data = json.data(using: .utf8)!
    var arrayProduct: [Test] = []
    let reader = try CSVReader(string: csv, hasHeaderRow: true)
    let decoder = CSVRowDecoder()
    
    while reader.next() != nil {
    //    let product: Product1 = try! JSONDecoder().decode(Product1.self, from: data)
        let product = try! decoder.decode(Product1.self, from: reader)
        arrayProduct.append(product)
        print("\(product)")
        sleep(4)
    }
    
    opened by a72 2
  • Date decoding causes memory leak

    Date decoding causes memory leak

    This code causes a memory leak.

    struct DecodableExample: Decodable {
        let dateKey: Date
        let stringKey: String
        let optionalStringKey: String?
    }
    [test.txt](https://github.com/yaslab/CSV.swift/files/2708834/test.txt)
    
    
    for i in 1...20 {
        var records = [DecodableExample]()
        var ii = 0
        do {
            let csv = try String(contentsOfFile: "/tmp/test.txt", encoding: .utf8)
            let reader = try CSVReader(string: csv, hasHeaderRow: true, trimFields: true, delimiter: ";", whitespaces: .whitespaces)
            let decoder = CSVRowDecoder()
            let formatter = DateFormatter()
            formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
            decoder.dateDecodingStrategy = .formatted(formatter)
            while reader.next() != nil {
                ii += 1
                let row = try decoder.decode(DecodableExample.self, from: reader)
                records.append(row)
            }
        } catch {
            print("\(ii)-\(error)")
        }
        print(i)
    }
    opened by a72 2
  • Added support for decoding with  Int-based CodingKey

    Added support for decoding with Int-based CodingKey

    Developer may now opt to implement Decodable using Int-based CodingKey, instead of String. This is particularly useful if the data does not have headers, or if the header strings are unwieldy.

    Example Decodable implementation:

    struct IntKeyedDecodableExample: Decodable, Equatable {
            private enum CodingKeys: Int, CodingKey {
                case intKey = 2
                case stringKey = 0
                case optionalStringKey = 1
                case dateKey = 4
                case enumKey = 5
            }
            
            let intKey: Int
            let stringKey: String
            let optionalStringKey: String?
            let dateKey: Date
            let enumKey: Enum
            
            func toRow() -> String {
                return "\(self.stringKey),\(self.optionalStringKey ?? ""),\(self.intKey),,\"\(CSVReader.dateFormatter.string(from: self.dateKey))\",\(self.enumKey)\n"
            }
            
            static func ==(left: IntKeyedDecodableExample, right: IntKeyedDecodableExample) -> Bool {
                let formatter = CSVReader.dateFormatter
                return left.intKey == right.intKey && left.stringKey == right.stringKey && left.optionalStringKey == right.optionalStringKey
                    //&& left.dateKey.compare(right.dateKey) == ComparisonResult.orderedSame // TODO: find more accurate conversion method, cannot compare directly likely because we are losing precision when in csv
                    && formatter.string(from: left.dateKey) == formatter.string(from: right.dateKey)
                    && left.enumKey == right.enumKey
            }
            
            static var examples: [IntKeyedDecodableExample] {
                return [
                    IntKeyedDecodableExample(intKey: 12345, stringKey: "stringValue", optionalStringKey: nil, dateKey: Date(), enumKey: .first),
                    IntKeyedDecodableExample(intKey: 54321, stringKey: "stringValue2", optionalStringKey: "withValue", dateKey: Date(timeInterval: 100, since: Date()), enumKey: .second)
                ]
            }
        }
    
    enhancement 
    opened by sstadelman 2
  • Support for Int-type CodingKey

    Support for Int-type CodingKey

    I've been working to add support & tests for Int typed CodingKey, for supporting Decodable with or without headers. Can I claim this feature if you're not already on it?

    Expected Behavior:

    • When implementing Decodable for a type, developer can use Int type enum for CodingKey
    • Value of the enum maps to column of the data
    • Header row should be ignored
    • Developer continues to use Configuration struct to indicate the presence/absence of header row

    Example:

        class IntKeyedDecodableExample: SupportedDecodableExample {
            private enum CodingKeys: Int, CodingKey {
                case intKey = 2
                case stringKey = 0
                case optionalStringKey = 1
                case dateKey = 4
                case enumKey = 5
            }
        }
    
    enhancement 
    opened by sstadelman 2
  • [Speed] Add buffer to BinaryReader

    [Speed] Add buffer to BinaryReader

    Improved speed of reading from file. (6a18924, 028bd2f)

    Result of the performance test

    • OS: Ubuntu 16.04
    • Swift: 4.2.1-RELEASE
    • Build Configuration: release
    • CSV file info
      • File size: 13.3MB (15310 records, 55 fields)
      • Encoding: UTF-8 (Including ASCII and Japanese)

    | | Before (v2.2.1) | After (this branch) | Delta | | ---- | ---- | ---- | ---- | | 1st | 35.313s | 5.811s | -83.5% | | 2nd | 35.964s | 5.746s | -84.0% | | 3rd | 35.672s | 5.732s | -83.9% |

    Test code

    // main.swift
    import Foundation
    import CSV
    
    let stream = InputStream(fileAtPath: "/path/to/file.csv")!
    let csv = try! CSVReader(stream: stream, hasHeaderRow: true)
    while csv.next() != nil {}
    
    opened by yaslab 2
  • GCD Support for Large Files?

    GCD Support for Large Files?

    Any way to speed this up with GCD? I have a CSV with 50k+ rows that I need to crunch through and just incrementing a counter takes about 45 seconds on my iPad.

    // Time began:
    let startTime = Date()
    
    // Read the CSV file:
    let filePath = Bundle.main.path(forResource: "foo", ofType: "csv")
    let stream = InputStream(fileAtPath: filePath!)
    let csv = try! CSVReader(stream: stream!, hasHeaderRow: true)
    
    // Count the rows:
    var counter = 0
    while csv.next() != nil {
        counter += 1
    }
    
    // Check the results:
    print("Rows: \(counter)")
    print("Elapsed time: \(startTime.timeIntervalSinceNow) seconds")
    
    Output:
    Rows: 52229
    Elapsed time: -46.1442109942436 seconds
    
    opened by nittanygeek 2
  • Newlines are handled incorrectly when there's an escaped quote

    Newlines are handled incorrectly when there's an escaped quote

    Ran into this issue when parsing a CSV file that contained JSON-like data with newlines. Example:

    "{(
        2fa,
        \"Apple Watch\"
    )}","Another column","A third column"
    

    From what it looks like, it gets split it into 3 rows.

    If you, however, replace the double quotes with single quotes, it works just fine:

    "Tags","Type","third"
    "{(
        2fa,
        \'Apple Watch\'
    )}","Another column","A third column"
    

    I know, it's a very specific edge case, but I just wanted to let you know

    opened by drallgood 0
  • How to upload CSV file to cloud?

    How to upload CSV file to cloud?

    Hi, thanks to your great effort. I really think the code is straightforward to use, especially for Swift beginners. I have successfully create a new CSV file via this method, and I'm curious about how to upload the file to the cloud? eg. google doc, or create a website to download? In addition, since my MacBook was produced in 2016, I just installed SPM and didn't even execute it, but it still took a long time. Is there any other simple version so that I can use the SPM more smoothly? Thanks a lot!

    opened by Jean-You 1
  • Poor Read and Parsing Performance

    Poor Read and Parsing Performance

    I am reading billions of rows of data from gzipped CSV files exported from Google Cloud Compute's BigQuery. Here is a Swift script using CSV.swift to read a gzipped CSV file with 12 columns and 848,563 rows:

    import Foundation
    
    import CSV
    import Gzip
    
    // CSV.swift
    // Takes 1m5s without decoding the objects
    // Takes 5m15s when decoding to objects
    func testReadBigQueryCSV() {
        let filePath = "data/my-data-export-2020-09-20-000000000161.csv.gz"
        let fileData: Data = try! Data(contentsOf: URL(fileURLWithPath: filePath))
        let decodedBody: Data = try! fileData.gunzipped()
        let stream = InputStream(data: decodedBody)
        let csv = try! CSVReader(stream: stream, hasHeaderRow: true)
        print(csv.headerRow!)
        var rows: [[String]] = []
        let decoder = CSVRowDecoder()
        decoder.userInfo[.knownFormatKey] = KnownFormat.BigQueryCSV
        for row in csv {
            rows.append(row)
        }
        /*var rows: [MyDecodableType] = []*/
        /*while csv.next() != nil {*/
            /*print("\(row)")*/
            /*let row: MyDecodableType = try! decoder.decode(MyDecodableType.self, from: csv)*/
            /*rows.append(row)*/
        /*}*/
        print("Got \(rows.count) rows")
    }
    

    When simply reading the CSV fields into an [[String]], it takes 1m5s. When decoding the Strings into types, it takes 5m15s.

    For comparison, here is a Python script that reads and parses the same file in 3.6 seconds:

    #!/usr/bin/env python3
    
    import pandas as pd
    
    # Takes 3.6 seconds when not parsing the dates
    # Takes 2m43s when parsing the dates
    def main():
        file_path = "data/my-data-export-2020-09-20-000000000161.csv.gz"
        print("Reading {}".format(file_path))
        # df = pd.read_csv(file_path, compression='gzip', parse_dates=["time", "timeUpdateReceived", "inserted"])
        df = pd.read_csv(file_path, compression='gzip')
        print("Read {} rows".format(df.shape))
        print(df.columns)
        print(df.dtypes)
        print(df.iloc[0])
    
    if __name__ == "__main__":
        main()
    
    

    The comparison is 3.6s vs 1m5s and 2m43s vs 5m15s. That's an 18x slower read. pandas also uses a single CPU core.

    opened by xanderdunn 0
  • Not parsing Properly if CSV row has HTML tags

    Not parsing Properly if CSV row has HTML tags

    If there is a space between HTML division tags, From the white space It consider that as separate row. I have attached screenshot of my sample data for reference. Could you please help me in this? I just want to know if there are specific formats needs to be followed for HTML texts in CSV.

    Screenshot 2020-08-27 at 10 27 20 PM
    opened by RNarenKrishnaa 0
  • error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0). The process has been left at the point where it was interrupted, use

    error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0). The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.

    Sorry if this is too simple but I'm quite new to Swift so this is all unfamiliar to me. I pasted this code into xcode and got the error above:

    import Foundation
    import CSV
    
    let stream = OutputStream(toFileAtPath: "file.csv", append: false)!
    let csv = try! CSVWriter(stream: stream)
    
    try! csv.write(row: ["id", "name"])
    try! csv.write(row: ["1", "foo"])
    try! csv.write(row: ["1", "bar"])
    
    csv.stream.close()
    

    Particularly it was pointing at this line: let csv = try! CSVWriter(stream: stream)

    In what seems to be the console, I got this:

    Fatal error: 'try!' expression unexpectedly raised an error: CSV.CSVError.cannotOpenFile: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/swiftlang_Fall2018/swiftlang_Fall2018-1000.11.42/src/swift/stdlib/public/core/ErrorType.swift, line 184

    opened by youssefavx 2
Releases(2.4.2)
  • 2.4.2(Jul 13, 2019)

    This release targets Swift 5.0.x / Xcode 10.2.x.

    Fixed

    • Fix a crash that occurs when the number of fields is small (#92). Thanks a72 for reporting this issue!
    • Fix CSV Deprecated compiler warning (#93). Thanks robwithhair for reporting this issue!

    Breaking change

    In 2.4.2 and later versions, If you specify a key that does not have a value in subscript, an empty string is returned. For example,

    let csvString = "key1,key2\nvalue1" // There is only one field in the second line.
    let csv = try! CSVReader(string: csvString, hasHeaderRow: true)
    csv.next()
    let value2 = csv["key2"] // Change point: Formerly nil, now "" is returned.
    let value3 = csv["key3"] // In this case, nil will be returned.
    
    Source code(tar.gz)
    Source code(zip)
  • 2.4.1(Jun 22, 2019)

  • 2.4.0(May 11, 2019)

  • 2.3.0(Dec 1, 2018)

    This release targets Swift 4.2.1 / Xcode 10.1.

    Added

    • Decodable support (#61). Please see README.md for usage. Thanks yoiang for the pull request!
    • Added support for decoding with Int-based CodingKey (#72). Thanks sstadelman for the pull request!

    Improved

    • Improved reading speed when using InputStream. (#65)
    Source code(tar.gz)
    Source code(zip)
  • 2.1.0(Sep 23, 2017)

  • 2.0.1(Jul 9, 2017)

  • 2.0.0(Jul 9, 2017)

  • 1.1.2(Nov 29, 2016)

  • 1.1.1(Nov 15, 2016)

  • 1.1.0(Nov 15, 2016)

  • 1.0.0(Sep 24, 2016)

  • 0.3.1(Aug 21, 2016)

  • 0.3.0(Aug 21, 2016)

    This release targets Swift 2.2 / Xcode 7.3.

    Breaking

    • Modified to use NSInputStream If you want to read the file. Please see the README.md for more details.

    Improved

    • Change the algorithm to read the csv string.
    Source code(tar.gz)
    Source code(zip)
Owner
Yasuhiro Hatta
iOS application developer
Yasuhiro Hatta
CSVParser - A swift library for fast read and write CSV file

CSVParser A swift library for fast read and write CSV file. This library supports all Apple platform and Linux. List to do get column by string subscr

Nero 86 Nov 22, 2022
CodableCSV - Read and write CSV files row-by-row or through Swift's Codable interface.

CodableCSV provides: Imperative CSV reader/writer. Declarative CSV encoder/decoder. Support multiple inputs/outputs: Strings, Data blobs, URLs, and St

Marcos Sánchez-Dehesa 369 Jan 8, 2023
Reading List - an iOS app to track personal reading lists

Reading List Reading List is an iOS app for iPhone and iPad which helps users track and catalog the books they read. Reading List v2 As of version 2.0

Andrew Bennet 297 Dec 28, 2022
An extension that simplifies the work with Swift AutoLayout by writing cleaner, more natural and easy-reading code.

Installation For now you're able to clone repo in your project or download ZIP folder manually. git clone https://github.com/votehserxam/AutoLayout.gi

Max 3 Nov 8, 2022
A Swift-based API for reading from & writing to the Apple System Log (more commonly known somewhat inaccurately as "the console")

CleanroomASL Notice: CleanroomASL is no longer supported The Apple System Log facility has been deprecated by Apple. As a result, we've deprecated Cle

Gilt Tech 62 Jan 29, 2022
A csv parser written in swift conforming to rfc4180

CSwiftV A csv parser conforming (and tested as much) to rfc4180 i.e the closest thing to a csv spec. It is currently all in memory so not suitable for

Daniel Haight 166 Dec 13, 2022
A simple, but efficient CSV Parser, written in Swift.

CSV CSV.swift is a powerful swift library for parsing CSV files that supports reading as [String], [String: String] and Decodable, without sacrificing

Ben Koska 4 Nov 28, 2022
CSVParser - A swift library for fast read and write CSV file

CSVParser A swift library for fast read and write CSV file. This library supports all Apple platform and Linux. List to do get column by string subscr

Nero 86 Nov 22, 2022
CodableCSV - Read and write CSV files row-by-row or through Swift's Codable interface.

CodableCSV provides: Imperative CSV reader/writer. Declarative CSV encoder/decoder. Support multiple inputs/outputs: Strings, Data blobs, URLs, and St

Marcos Sánchez-Dehesa 369 Jan 8, 2023
Taking a string containing a csv file and split it into records (aka lines) containing fields of data (aka Array of SubStrings)

Swift .csv parser Taking a string containing a csv file and split it into records (aka lines) containing fields of data (aka Array of SubStrings). Par

Matthias 0 Dec 29, 2021
A command line tool to parse pricing from a pdf and generate an updated csv file for House Call Pro

A command line tool to parse pricing from a pdf and generate an updated csv file for House Call Pro

hhe-dev 10 Feb 17, 2022
Converter for your Rigol Oscilloscope .CSV files to LtSpice

rigol2spice A program to convert Rigol oscilloscope's .CSV files to a format readable by LTspice. Your Rigol oscilloscope can output .CSV files that c

Rui Carneiro 4 Aug 31, 2022
Hackers is an elegant iOS app for reading Hacker News written in Swift.

Hackers Hackers is an iPhone and iPad app for reading Hacker News on the go. It's optimised for quickly catching up on the latest news and comments wi

Weiran Zhang 628 Dec 26, 2022
TPInAppReceipt is a lightweight, pure-Swift library for reading and validating Apple In App Purchase Receipt locally.

TPInAppReceipt is a lightweight, pure-Swift library for reading and validating Apple In App Purchase Receipt locally. Features Read all

Pavel T 520 Jan 4, 2023
Extensions and classes in Swift that make it easy to get an iOS device reading and processing MIDI data

MorkAndMIDI A really thin Swift layer on top of CoreMIDI that opens a virtual MIDI destination and port and connects to any MIDI endpoints that appear

Brad Howes 11 Nov 5, 2022
Reading animation allows you to click on the different page numbers and accordingly it will animate page changes in a cool way. It has a very attractive UI and is very easy to use.

Reading Animation Cool Reading Animation in iOS written in Swift. Preview Table of content :- Description How to add in your project Requirement Licen

MindInventory 42 Oct 4, 2022
Reading List is an iOS app for iPhone and iPad which helps users track and catalog the books they read

Reading List Reading List is an iOS app for iPhone and iPad which helps users track and catalog the books they read. Reading List v2 As of version 2.0

Andrew Bennet 281 Jan 15, 2022
My english learning by reading application swift, realm

read-answer-learn-app My english learning by reading application swift, realm I used Swift language and storyboard, realm, tureng translate service in

null 4 Aug 18, 2022
UDPReader : a simple package for reading udp packets

UDPReader is a simple package for reading udp packets

leroy 2 Aug 7, 2022
A digital BookShelf for your reading progress.

BookSearch a flutter medium series What is this? An open source app which revolves all around books: “Collecting books as you read them, virtually”. I

Norbert Kozsir 512 Dec 29, 2022