https://github.com/flinedev/csvimporter
Import CSV files line by line with ease
https://github.com/flinedev/csvimporter
csv csv-files data-mapping header importer line-by-line
Last synced: 3 months ago
JSON representation
Import CSV files line by line with ease
- Host: GitHub
- URL: https://github.com/flinedev/csvimporter
- Owner: FlineDev
- License: mit
- Created: 2016-01-16T17:09:37.000Z (over 9 years ago)
- Default Branch: main
- Last Pushed: 2023-09-26T06:28:43.000Z (about 2 years ago)
- Last Synced: 2025-06-05T23:08:38.806Z (4 months ago)
- Topics: csv, csv-files, data-mapping, header, importer, line-by-line
- Language: Swift
- Homepage:
- Size: 8.13 MB
- Stars: 152
- Watchers: 13
- Forks: 29
- Open Issues: 10
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
![]()
Installation
• Usage
• Donation
• Issues
• Contributing
• License# CSVImporter
Import CSV files line by line with ease.
## Rationale
"Why yet another CSVImporter" you may ask. "There is already [SwiftCSV](https://github.com/naoty/SwiftCSV) and [CSwiftV](https://github.com/Daniel1of1/CSwiftV)" you may say. The truth is that these frameworks work well for **smaller** CSV files. But once you have a really **large CSV file** (or *could* have one, because you let the user import whatever CSV file he desires to) then those solutions will probably cause **delays and memory issues** for some of your users.
**CSVImporter** on the other hand works both **asynchronously** (prevents delays) and reads your CSV file **line by line** instead of loading the entire String into memory (prevents memory issues). On top of that it is **easy to use** and provides **beautiful callbacks** for indicating failure, progress, completion and even **data mapping** if you desire to.
## Installation
Currently the recommended way of installing this library is via [Carthage](https://github.com/Carthage/Carthage) on macOS or [Swift Package Manager](https://github.com/apple/swift-package-manager) on Linux. [Cocoapods](https://github.com/CocoaPods/CocoaPods) might work, too, but is not tested.
You can of course also just include this framework manually into your project by downloading it or by using git submodules.
## Usage
Please have a look at the UsageExamples.playground and the Tests/CSVImporterTests/CSVImporterSpec.swift files for a complete list of features provided.
Open the Playground from within the `.xcworkspace` in order for it to work.### Basic CSV Import
First create an instance of CSVImporter and specify the type the data within a line from the CSV should have. The default data type is an array of `String` objects which would look like this:
``` Swift
let path = "path/to/your/CSV/file"
let importer = CSVImporter<[String]>(path: path)
importer.startImportingRecords { $0 }.onFinish { importedRecords in
for record in importedRecords {
// record is of type [String] and contains all data in a line
}
}
```Note that you can specify an **alternative delimiter** when creating a `CSVImporter` object alongside the path. The delimiter defaults to `,` if you don't specify any.
### Asynchronous with Callbacks
CSVImporter works asynchronously by default and therefore doesn't block the main thread. As you can see the `onFinish` method is called once it finishes for using the results. There is also `onFail` for failure cases (for example when the given path doesn't contain a CSV file), `onProgress` which is regularly called and provides the number of lines already processed (e.g. for progress indicators). You can chain them as follows:
``` Swift
importer.startImportingRecords { $0 }.onFail {print("The CSV file couldn't be read.")
}.onProgress { importedDataLinesCount in
print("\(importedDataLinesCount) lines were already imported.")
}.onFinish { importedRecords in
print("Did finish import with \(importedRecords.count) records.")
}
```By default the real importing work is done in the `.utility` global background queue and callbacks are called on the `main` queue. This way the hard work is done asynchronously but the callbacks allow you to update your UI. If you need a different behavior, you can customize the queues when creating a CSVImporter object like so:
``` Swift
let path = "path/to/your/CSV/file"
let importer = CSVImporter<[String]>(path: path, workQosClass: .background, callbacksQosClass: .utility)
```### Import Synchronously
If you know your file is small enough or blocking the UI is not a problem, you can also use the synchronous import methods to import your data. Simply call `importRecords` instead of `startImportingRecords` and you will receive the end result (the same content as in the `onFinish` closure when using `startImportingRecords`) directly:
``` Swift
let importedRecords = importer.importRecords { $0 }
```Note that this method doesn't have any option to get notified about progress or failure – you just get the result. Check if the resulting array is empty to recognize potential failures.
### Easy data mapping
As stated above the default type is a `[String]` but you can provide whatever type you like. For example, let's say you have a class like this
``` Swift
class Student {
let firstName: String, lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
}
```and your CSV file looks something like the following
``` CSV
Harry,Potter
Hermione,Granger
Ron,Weasley
```then you can specify a mapper as the closure instead of the `{ $0 }` from the examples above like this:
``` Swift
let path = "path/to/Hogwarts/students"
let importer = CSVImporter(path: path)
importer.startImportingRecords { recordValues -> Student inreturn Student(firstName: recordValues[0], lastName: recordValues[1])
}.onFinish { importedRecords in
for student in importedRecords {
// Now importedRecords is an array of Students
}}
```### Header Structure Support
Last but not least some CSV files have the structure of the data specified within the first line like this:
``` CSV
firstName,lastName
Harry,Potter
Hermione,Granger
Ron,Weasley
```In that case CSVImporter can automatically provide each record as a dictionary like this:
``` Swift
let path = "path/to/Hogwarts/students"
let importer = CSVImporter<[String: String]>(path: path)
importer.startImportingRecords(structure: { (headerValues) -> Void inprint(headerValues) // => ["firstName", "lastName"]
}) { $0 }.onFinish { importedRecords in
for record in importedRecords {
print(record) // => e.g. ["firstName": "Harry", "lastName": "Potter"]
print(record["firstName"]) // prints "Harry" on first, "Hermione" on second run
print(record["lastName"]) // prints "Potter" on first, "Granger" on second run
}}
```Note: If a records values count doesn't match that of the first lines values count then the record will be ignored.
## Donation
BartyCrouch was brought to you by [Cihat Gündüz](https://github.com/Jeehut) in his free time. If you want to thank me and support the development of this project, please **make a small donation on [PayPal](https://paypal.me/Dschee/5EUR)**. In case you also like my other [open source contributions](https://github.com/Flinesoft) and [articles](https://medium.com/@Jeehut), please consider motivating me by **becoming a sponsor on [GitHub](https://github.com/sponsors/Jeehut)** or a **patron on [Patreon](https://www.patreon.com/Jeehut)**.
Thank you very much for any donation, it really helps out a lot! 💯
## Contributing
See the file [CONTRIBUTING.md](https://github.com/Flinesoft/CSVImporter/blob/stable/CONTRIBUTING.md).
## License
This library is released under the [MIT License](http://opensource.org/licenses/MIT). See LICENSE for details.