Skip to content

Commit

Permalink
Merge branch 'deploy/1.7.0' into productive
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeehut committed Feb 13, 2017
2 parents 319bae2 + e2787e1 commit 52ca36e
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 9 deletions.
2 changes: 1 addition & 1 deletion CSVImporter.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|

s.name = "CSVImporter"
s.version = "1.6.0"
s.version = "1.7.0"
s.summary = "Import CSV files line by line with ease."

s.description = <<-DESC
Expand Down
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
alt="codebeat badge">
</a>
<a href="https://github.com/Flinesoft/CSVImporter/releases">
<img src="https://img.shields.io/badge/Version-1.6.0-blue.svg"
alt="Version: 1.6.0">
<img src="https://img.shields.io/badge/Version-1.7.0-blue.svg"
alt="Version: 1.7.0">
</a>
<img src="https://img.shields.io/badge/Swift-3-FFAC45.svg"
alt="Swift: 3">
Expand Down Expand Up @@ -56,7 +56,7 @@ You can of course also just include this framework manually into your project by
Simply add this line to your Cartfile:

```
github "Flinesoft/CSVImporter" ~> 1.6
github "Flinesoft/CSVImporter" ~> 1.7
```

And run `carthage update`. Then drag & drop the HandySwift.framework in the Carthage/build folder to your project. Also do the same with the dependent framework `HandySwift`. Now you can `import CSVImporter` in each class you want to use its features. Refer to the [Carthage README](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application) for detailed / updated instructions.
Expand All @@ -71,7 +71,7 @@ platform :ios, '8.0'
use_frameworks!

target 'MyAppTarget' do
pod 'CSVImporter', '~> 1.6'
pod 'CSVImporter', '~> 1.7'
end
```

Expand Down Expand Up @@ -128,6 +128,16 @@ 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
Expand Down
53 changes: 51 additions & 2 deletions Sources/Code/CSVImporter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ public class CSVImporter<T> {
/// - Returns: `self` to enable consecutive method calls (e.g. `importer.startImportingRecords {...}.onProgress {...}`).
public func startImportingRecords(mapper closure: @escaping (_ recordValues: [String]) -> T) -> Self {
workDispatchQueue.async {
var importedRecords: [T] = []
var importedRecords = [T]()

let importedLinesWithSuccess = self.importLines { valuesInLine in
let newRecord = closure(valuesInLine)
Expand Down Expand Up @@ -156,7 +156,7 @@ public class CSVImporter<T> {
recordMapper closure: @escaping (_ recordValues: [String: String]) -> T) -> Self {
workDispatchQueue.async {
var recordStructure: [String]?
var importedRecords: [T] = []
var importedRecords = [T]()

let importedLinesWithSuccess = self.importLines { valuesInLine in

Expand Down Expand Up @@ -185,6 +185,55 @@ public class CSVImporter<T> {
return self
}

/// Synchronously imports all records and provides the end result only.
///
/// Use the `startImportingRecords` method for an asynchronous import with progress, fail and finish callbacks.
///
/// - Parameters:
/// - mapper: A closure to map the data received in a line to your data structure.
/// - Returns: The imported records array.
public func importRecords(mapper closure: @escaping (_ recordValues: [String]) -> T) -> [T] {
var importedRecords = [T]()

_ = self.importLines { valuesInLine in
let newRecord = closure(valuesInLine)
importedRecords.append(newRecord)
}

return importedRecords
}

/// Synchronously imports all records and provides the end result only.
///
/// Use the `startImportingRecords` method for an asynchronous import with progress, fail and finish callbacks.
///
/// - structure: A closure for doing something with the found structure within the first line of the CSV file.
/// - recordMapper: A closure to map the dictionary data interpreted from a line to your data structure.
/// - Returns: The imported records array.
public func importRecords(structure structureClosure: @escaping (_ headerValues: [String]) -> Void,
recordMapper closure: @escaping (_ recordValues: [String: String]) -> T) -> [T] {

var recordStructure: [String]?
var importedRecords = [T]()

_ = self.importLines { valuesInLine in

if recordStructure == nil {
recordStructure = valuesInLine
structureClosure(valuesInLine)
} else {
if let structuredValuesInLine = [String: String](keys: recordStructure!, values: valuesInLine) {
let newRecord = closure(structuredValuesInLine)
importedRecords.append(newRecord)
} else {
print("CSVImporter – Warning: Couldn't structurize line.")
}
}
}

return importedRecords
}

/// Imports all lines one by one and
///
/// - Parameters:
Expand Down
2 changes: 1 addition & 1 deletion Sources/Supporting Files/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.6.0</string>
<string>1.7.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
Expand Down
29 changes: 29 additions & 0 deletions Tests/Code/CSVImporterSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,18 @@ class CSVImporterSpec: QuickSpec { // swiftlint:disable:this type_body_length
expect(recordValues).toEventuallyNot(beNil(), timeout: 10)
}

it("imports data from CSV file without headers synchronously") {
let path = Bundle(for: CSVImporterSpec.self).path(forResource: "Teams", ofType: "csv")
var recordValues: [[String]]?

if let path = path {
let importer = CSVImporter<[String]>(path: path)
recordValues = importer.importRecords { $0 }
}

expect(recordValues).notTo(beNil())
}

it("imports data from CSV file special characters") {
let path = Bundle(for: CSVImporterSpec.self).path(forResource: "CommaSemicolonQuotes", ofType: "csv")
var recordValues: [[String]]?
Expand Down Expand Up @@ -114,6 +126,23 @@ class CSVImporterSpec: QuickSpec { // swiftlint:disable:this type_body_length
expect(recordValues!.first!).toEventually(equal(self.validTeamsFirstRecord()))
}

it("imports data from CSV file with headers synchronously") {
let path = Bundle(for: CSVImporterSpec.self).path(forResource: "Teams", ofType: "csv")
var recordValues: [[String: String]]?

if let path = path {
let importer = CSVImporter<[String: String]>(path: path)
recordValues = importer.importRecords(structure: { (headerValues) -> Void in
print(headerValues)
}, recordMapper: { (recordValues) -> [String : String] in
return recordValues
})
}

expect(recordValues).notTo(beNil())
expect(recordValues!.first!).to(equal(self.validTeamsFirstRecord()))
}

it("imports data from CSV file content string with headers") {
let path = Bundle(for: CSVImporterSpec.self).path(forResource: "Teams", ofType: "csv")
let contentString = try! String(contentsOfFile: path!) // swiftlint:disable:this force_try
Expand Down
6 changes: 6 additions & 0 deletions UsageExamples.playground/Contents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ stringImporter.startImportingRecords { $0 }.onFinish { importedRecords in
importedRecords // array with all records (in this case an array of arrays)
}

//: ### Synchronous import: .importRecords
//: Line-by-line import which results in the end result immediately. Works synchronously.

let results = defaultImporter.importRecords { $0 }
results

//: ### .onFail
//: In case your path was wrong the chainable `.onFail` callback will be called instead of the `.onFinish`.

Expand Down
2 changes: 1 addition & 1 deletion UsageExamples.playground/timeline.xctimeline
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
version = "3.0">
<TimelineItems>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=79&amp;CharacterRangeLoc=2246&amp;EndingColumnNumber=84&amp;EndingLineNumber=55&amp;StartingColumnNumber=5&amp;StartingLineNumber=55&amp;Timestamp=508112162.192021"
documentLocation = "#CharacterRangeLen=79&amp;CharacterRangeLoc=2440&amp;EndingColumnNumber=84&amp;EndingLineNumber=61&amp;StartingColumnNumber=5&amp;StartingLineNumber=61&amp;Timestamp=508683581.980473"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
Expand Down

0 comments on commit 52ca36e

Please sign in to comment.