Skip to content

Commit 52ca36e

Browse files
committed
Merge branch 'deploy/1.7.0' into productive
2 parents 319bae2 + e2787e1 commit 52ca36e

File tree

7 files changed

+103
-9
lines changed

7 files changed

+103
-9
lines changed

CSVImporter.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Pod::Spec.new do |s|
22

33
s.name = "CSVImporter"
4-
s.version = "1.6.0"
4+
s.version = "1.7.0"
55
s.summary = "Import CSV files line by line with ease."
66

77
s.description = <<-DESC

README.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
alt="codebeat badge">
1313
</a>
1414
<a href="https://github.com/Flinesoft/CSVImporter/releases">
15-
<img src="https://img.shields.io/badge/Version-1.6.0-blue.svg"
16-
alt="Version: 1.6.0">
15+
<img src="https://img.shields.io/badge/Version-1.7.0-blue.svg"
16+
alt="Version: 1.7.0">
1717
</a>
1818
<img src="https://img.shields.io/badge/Swift-3-FFAC45.svg"
1919
alt="Swift: 3">
@@ -56,7 +56,7 @@ You can of course also just include this framework manually into your project by
5656
Simply add this line to your Cartfile:
5757

5858
```
59-
github "Flinesoft/CSVImporter" ~> 1.6
59+
github "Flinesoft/CSVImporter" ~> 1.7
6060
```
6161

6262
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.
@@ -71,7 +71,7 @@ platform :ios, '8.0'
7171
use_frameworks!
7272

7373
target 'MyAppTarget' do
74-
pod 'CSVImporter', '~> 1.6'
74+
pod 'CSVImporter', '~> 1.7'
7575
end
7676
```
7777

@@ -128,6 +128,16 @@ let path = "path/to/your/CSV/file"
128128
let importer = CSVImporter<[String]>(path: path, workQosClass: .background, callbacksQosClass: .utility)
129129
```
130130

131+
### Import Synchronously
132+
133+
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:
134+
135+
``` Swift
136+
let importedRecords = importer.importRecords { $0 }
137+
```
138+
139+
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.
140+
131141
### Easy data mapping
132142

133143
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

Sources/Code/CSVImporter.swift

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ public class CSVImporter<T> {
127127
/// - Returns: `self` to enable consecutive method calls (e.g. `importer.startImportingRecords {...}.onProgress {...}`).
128128
public func startImportingRecords(mapper closure: @escaping (_ recordValues: [String]) -> T) -> Self {
129129
workDispatchQueue.async {
130-
var importedRecords: [T] = []
130+
var importedRecords = [T]()
131131

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

161161
let importedLinesWithSuccess = self.importLines { valuesInLine in
162162

@@ -185,6 +185,55 @@ public class CSVImporter<T> {
185185
return self
186186
}
187187

188+
/// Synchronously imports all records and provides the end result only.
189+
///
190+
/// Use the `startImportingRecords` method for an asynchronous import with progress, fail and finish callbacks.
191+
///
192+
/// - Parameters:
193+
/// - mapper: A closure to map the data received in a line to your data structure.
194+
/// - Returns: The imported records array.
195+
public func importRecords(mapper closure: @escaping (_ recordValues: [String]) -> T) -> [T] {
196+
var importedRecords = [T]()
197+
198+
_ = self.importLines { valuesInLine in
199+
let newRecord = closure(valuesInLine)
200+
importedRecords.append(newRecord)
201+
}
202+
203+
return importedRecords
204+
}
205+
206+
/// Synchronously imports all records and provides the end result only.
207+
///
208+
/// Use the `startImportingRecords` method for an asynchronous import with progress, fail and finish callbacks.
209+
///
210+
/// - structure: A closure for doing something with the found structure within the first line of the CSV file.
211+
/// - recordMapper: A closure to map the dictionary data interpreted from a line to your data structure.
212+
/// - Returns: The imported records array.
213+
public func importRecords(structure structureClosure: @escaping (_ headerValues: [String]) -> Void,
214+
recordMapper closure: @escaping (_ recordValues: [String: String]) -> T) -> [T] {
215+
216+
var recordStructure: [String]?
217+
var importedRecords = [T]()
218+
219+
_ = self.importLines { valuesInLine in
220+
221+
if recordStructure == nil {
222+
recordStructure = valuesInLine
223+
structureClosure(valuesInLine)
224+
} else {
225+
if let structuredValuesInLine = [String: String](keys: recordStructure!, values: valuesInLine) {
226+
let newRecord = closure(structuredValuesInLine)
227+
importedRecords.append(newRecord)
228+
} else {
229+
print("CSVImporter – Warning: Couldn't structurize line.")
230+
}
231+
}
232+
}
233+
234+
return importedRecords
235+
}
236+
188237
/// Imports all lines one by one and
189238
///
190239
/// - Parameters:

Sources/Supporting Files/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<key>CFBundlePackageType</key>
1616
<string>FMWK</string>
1717
<key>CFBundleShortVersionString</key>
18-
<string>1.6.0</string>
18+
<string>1.7.0</string>
1919
<key>CFBundleSignature</key>
2020
<string>????</string>
2121
<key>CFBundleVersion</key>

Tests/Code/CSVImporterSpec.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,18 @@ class CSVImporterSpec: QuickSpec { // swiftlint:disable:this type_body_length
5858
expect(recordValues).toEventuallyNot(beNil(), timeout: 10)
5959
}
6060

61+
it("imports data from CSV file without headers synchronously") {
62+
let path = Bundle(for: CSVImporterSpec.self).path(forResource: "Teams", ofType: "csv")
63+
var recordValues: [[String]]?
64+
65+
if let path = path {
66+
let importer = CSVImporter<[String]>(path: path)
67+
recordValues = importer.importRecords { $0 }
68+
}
69+
70+
expect(recordValues).notTo(beNil())
71+
}
72+
6173
it("imports data from CSV file special characters") {
6274
let path = Bundle(for: CSVImporterSpec.self).path(forResource: "CommaSemicolonQuotes", ofType: "csv")
6375
var recordValues: [[String]]?
@@ -114,6 +126,23 @@ class CSVImporterSpec: QuickSpec { // swiftlint:disable:this type_body_length
114126
expect(recordValues!.first!).toEventually(equal(self.validTeamsFirstRecord()))
115127
}
116128

129+
it("imports data from CSV file with headers synchronously") {
130+
let path = Bundle(for: CSVImporterSpec.self).path(forResource: "Teams", ofType: "csv")
131+
var recordValues: [[String: String]]?
132+
133+
if let path = path {
134+
let importer = CSVImporter<[String: String]>(path: path)
135+
recordValues = importer.importRecords(structure: { (headerValues) -> Void in
136+
print(headerValues)
137+
}, recordMapper: { (recordValues) -> [String : String] in
138+
return recordValues
139+
})
140+
}
141+
142+
expect(recordValues).notTo(beNil())
143+
expect(recordValues!.first!).to(equal(self.validTeamsFirstRecord()))
144+
}
145+
117146
it("imports data from CSV file content string with headers") {
118147
let path = Bundle(for: CSVImporterSpec.self).path(forResource: "Teams", ofType: "csv")
119148
let contentString = try! String(contentsOfFile: path!) // swiftlint:disable:this force_try

UsageExamples.playground/Contents.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ stringImporter.startImportingRecords { $0 }.onFinish { importedRecords in
3333
importedRecords // array with all records (in this case an array of arrays)
3434
}
3535

36+
//: ### Synchronous import: .importRecords
37+
//: Line-by-line import which results in the end result immediately. Works synchronously.
38+
39+
let results = defaultImporter.importRecords { $0 }
40+
results
41+
3642
//: ### .onFail
3743
//: In case your path was wrong the chainable `.onFail` callback will be called instead of the `.onFinish`.
3844

UsageExamples.playground/timeline.xctimeline

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
version = "3.0">
44
<TimelineItems>
55
<LoggerValueHistoryTimelineItem
6-
documentLocation = "#CharacterRangeLen=79&amp;CharacterRangeLoc=2246&amp;EndingColumnNumber=84&amp;EndingLineNumber=55&amp;StartingColumnNumber=5&amp;StartingLineNumber=55&amp;Timestamp=508112162.192021"
6+
documentLocation = "#CharacterRangeLen=79&amp;CharacterRangeLoc=2440&amp;EndingColumnNumber=84&amp;EndingLineNumber=61&amp;StartingColumnNumber=5&amp;StartingLineNumber=61&amp;Timestamp=508683581.980473"
77
selectedRepresentationIndex = "0"
88
shouldTrackSuperviewWidth = "NO">
99
</LoggerValueHistoryTimelineItem>

0 commit comments

Comments
 (0)