I have an array as below with 13 items.
var data =[“MonthFolder/",
"MonthFolder/January",
"MonthFolder/February",
"MonthFolder/March",
"DayFolder/",
"DayFolder/Monday",
"DayFolder/Tuesday",
"DayFolder/Wednesday",
"DayFolder/Thursday",
"YearFolder/",
"YearFolder/2016",
"YearFolder/2015",
"YearFolder/2014"]
I am trying to print an array with end result as below
[("MonthFolder/January","MonthFolder/February","MonthFolder/March"),
("DayFolder/Monday","DayFolder/Tuesday","DayFolder/Wednesday","DayFolder/Thursday"),
("YearFolder/2016", "YearFolder/2015”, "YearFolder/2014")]”
I have done this
var fileName = [AnyObject]()
var allFiles = [AnyObject]()
for item in data{
let keyString = item.characters.last
if keyString != "/"{
fileName.append(item)
}
else if keyString == "/"{
if fileName.count > 0{
allFiles.append(fileName)
fileName = []
}
}
}
and got the output as
[("MonthFolder/January","MonthFolder/February","MonthFolder/March"),
("DayFolder/Monday","DayFolder/Tuesday","DayFolder/Wednesday","DayFolder/Thursday”)]
missing the third object in the allFiles because the else if condition fails for the last iteration. How do i achieve my desired output? Thank you.
By this way you will get expected output :
var fileName = [AnyObject]()
var allFiles = [AnyObject]()
for item in data{
let keyString = item.characters.last
if keyString != "/"{
fileName.append(item)
}
else if keyString == "/"{
if fileName.count > 0{
allFiles.append(fileName)
fileName = []
}
}
}
if fileName.count != 0 {
allFiles.append(fileName)
fileName = []
}
Hope this will help you :)
Try this
data.forEach { (item) -> () in
if item.characters.last != "/" {
fileName.append(item)
} else if fileName.count > 0 {
allFiles.append(fileName)
fileName = []
}
}
allFiles.append(fileName)
print(allFiles)
I have tried this
var data = ["MonthFolder/", "MonthFolder/January", "MonthFolder/February", "MonthFolder/March", "DayFolder/", "DayFolder/Monday", "DayFolder/Tuesday", "DayFolder/Wednesday", "DayFolder/Thursday", "YearFolder/", "YearFolder/2016", "YearFolder/2015", "YearFolder/2014"];
var fileName = []
for (item in data) {
var keyString = data[item].split("/");
if(keyString[1] == "")
{
var keyV = keyString[0];
fileName[keyV]= new Array();
}
else
{
fileName[keyV].push(data[item]);
}
}
console.log(fileName)
and i got output like
[
DayFolder
["DayFolder/Monday", "DayFolder/Tuesday", "DayFolder/Wednesday", "DayFolder/Thursday"]
MonthFolder
["MonthFolder/January", "MonthFolder/February", "MonthFolder/March"]
YearFolder
["YearFolder/2016", "YearFolder/2015", "YearFolder/2014"]
]
Please check
Functional way:
var data = ["MonthFolder/",
"MonthFolder/January",
"MonthFolder/February",
"MonthFolder/March",
"DayFolder/",
"DayFolder/Monday",
"DayFolder/Tuesday",
"DayFolder/Wednesday",
"DayFolder/Thursday",
"YearFolder/",
"YearFolder/2016",
"YearFolder/2015",
"YearFolder/2014"]
var allFolders = [String]()
var allFiles = [String]()
func gatherFiles(files: [String], inFolders folders: [String]) -> [[String]] {
return folders.map { folder in
return (files.filter { $0.containsString(folder)})
}
}
for item in data {
let components = item.componentsSeparatedByString("/").filter({!$0.isEmpty})
if components.count > 1 {
allFiles.append(item)
} else {
allFolders.append(item)
}
}
let finalArr = gatherFiles(allFiles, inFolders: allFolders)
Note
Cons: with this way, there's more iterations.
Pros: You could reuse it ?
You can also use more generic approach based on Dictionary struct
var filesDict = [String : [String]]()
for fileName in data {
let fileComponents = fileName.componentsSeparatedByString("/")
let fileKey = fileComponents[0]
let fileValue = fileComponents[1]
var itemsArray = filesDict[fileKey]
if itemsArray == nil {
itemsArray = []
}
if fileValue != "" {
itemsArray!.append(fileName)
}
filesDict[fileKey] = itemsArray
}
//conversion from Dictionary to Array
let outputArray = Array(filesDict.values)
print(outputArray)
output
[["MonthFolder/January", "MonthFolder/February", "MonthFolder/March"],
["DayFolder/Monday", "DayFolder/Tuesday", "DayFolder/Wednesday", "DayFolder/Thursday"],
["YearFolder/2016", "YearFolder/2015", "YearFolder/2014"]]
Related
I had to upgrade from AudioKit 4.0 to AudioKit 4.2 due to an incompatibility of AudioKit 4.0 with latest swift language and Xcode.
However, now my project cannot be compiled because loadMelodicSoundFont is not a member of AKSampler anymore while I'm using this method to load sf2 sound file.
I could find a documentation for 4.1 only on http://audiokit.io/docs/ and 4.1 has loadMelodicSoundFont apparently. And no documentation for 4.2 I could find.
So what is the replacement for this method in AudioKit 4.2?
The new AKSampler class in the latest versions of AudioKit is an entirely new custom sampler. However, as of right now it no longer handles SoundFont files natively (that's what your sf2 file is).
The easiest way for you would simply be to switch to AKAppleSampler, which is the fully featured sampler from previous versions of AudioKit. It relies on the Apple AU code and is still able to load SoundFont files.
So in practice you would simply rename your references to AKSampler to AKAppleSampler in your code.
You can rewrite the loadBetterSFZ sampler-extension function in the AKsampler example. This only reads for the included converted files and presumes the sample file is the last part of a region.
I did so and now I can use to sfz converted sf2 files from polyphony(windows) and sforzando and read the file for as far as I understand AKSampler can hold. I made a class to read the sfz file into a data structure that holds the data for reuse. For the sampler I wrote an extension that loads from the created data. Its all quick and dirty (I am not a programmer!). There are many different ways sfz files seem to be structured and I only tried a dozen or so.
example: add class SFZData to conductor in the example
get data with func SFZData.parseSFZ(folderPath: String, sfzFileName: String)->SFZ?
use SFZ for the extension in sampler: func loadSFZData(sfz:SFZ)
//here is what I did(all last versions and works on iPhone 6s)
import Foundation
class regionData {
var lovel: Int32 = -1 //not set, use group
var hivel: Int32 = -1
var lokey: Int32 = -1
var hikey: Int32 = -1
var pitch: Int32 = -1
var tune: Int32 = 0
var transpose: Int32 = 0
var loopmode: String = ""
var loopstart: Float32 = 0
var loopend: Float32 = 0
var startPoint: Float32 = 0
var endPoint: Float32 = 0
var sample: String = ""
init(){
}
}
class groupData {
var lovel: Int32 = 0
var hivel: Int32 = 127
var lokey: Int32 = 0
var hikey: Int32 = 127
var pitch: Int32 = 60
var loopmode: String = ""
var sample: String = ""
var regions = [regionData]()
init(){
}
}
class globalData {
var samplePath = ""
var lovel: Int32 = 0
var hivel: Int32 = 127
var sample: String = ""
var groups = [groupData]()
//ampdata?
//filterdata?
init(){
}
}
class ampData {
// in global and or group?
}
class SFZ {
var sfzName = ""
var baseURL : URL!
var global = globalData()
var group = [groupData?]()
init(){
//
}
}
class SFZdata {
var sfzChuncks = [String:SFZ]()
init(){
}
func getData(folderPath: String, sfzFileName: String)->SFZ?{
let sfzdata = sfzChuncks[sfzFileName]
if sfzdata != nil {
return sfzdata
}
return parseSFZ(folderPath:folderPath,sfzFileName:sfzFileName)
}
func parseSFZ(folderPath: String, sfzFileName: String)->SFZ? {
//let globalName = "<global>"
//let groupName = "<group>"
let regionName = "<region>"
var filePosition : String.Index
var chunck = ""
var data: String
let sfz = SFZ()
//stopAllVoices()
//unloadAllSamples()
let baseURL = URL(fileURLWithPath: folderPath)
let sfzURL = baseURL.appendingPathComponent(sfzFileName)
do {
data = try String(contentsOf: sfzURL, encoding: .ascii)
}catch {
debugPrint("file not found")
return nil
}
sfz.sfzName = sfzFileName
filePosition = data.startIndex
while filePosition != data.endIndex {
chunck = findHeader(data: data,dataPointer: &filePosition)
switch chunck {
case "<global>":
//get end of gobal and read data
let globaldata = readChunck(data: data, dataPointer: &filePosition)
let trimmed = String(globaldata.trimmingCharacters(in: .whitespacesAndNewlines))
sfz.global = readGlobal(globalChunck: trimmed)!
break
case "<group>":
//get end of group and read data
//first read this one the
let groupdata = readChunck(data: data, dataPointer: &filePosition)
let trimmed = String(groupdata.trimmingCharacters(in: .whitespacesAndNewlines))
let mygroup = readGroup(groupChunck: trimmed)
chunck = findHeader(data: data, dataPointer: &filePosition)
while chunck == regionName {
//read region and append
let regiondata = readChunck(data: data, dataPointer: &filePosition)
let trimmed = String(regiondata.trimmingCharacters(in: .whitespacesAndNewlines))
let myRegion = readRegion(regionChunck: trimmed)
mygroup?.regions.append(myRegion)
chunck = findHeader(data: data, dataPointer: &filePosition)
}
if chunck != regionName && filePosition != data.endIndex {
//back to before header if ! endoffile
filePosition = data.index(filePosition, offsetBy: -(chunck.count))
}
sfz.group.append(mygroup)
break
// case region without group ? ignore
default:
//ignore
break
}
}
sfz.baseURL = URL(fileURLWithPath: folderPath)
sfzChuncks.updateValue(sfz, forKey: sfzFileName)
return sfz
}
func findHeader(data:String, dataPointer:inout String.Index)->(String) {
if dataPointer == data.endIndex {
return ("")
}
while dataPointer != data.endIndex {
if data[dataPointer] == "<" { break }
dataPointer = data.index(after: dataPointer)
}
if dataPointer == data.endIndex {
return ("")
}
let start = dataPointer
while dataPointer != data.endIndex {
if data[dataPointer] == ">" { break }
dataPointer = data.index(after: dataPointer)
}
dataPointer = data.index(after: dataPointer)
if dataPointer == data.endIndex {
return ("")
}
return (String(data[start..<dataPointer]))
}
func readChunck(data:String,dataPointer:inout String.Index)->String{
var readData = ""
if dataPointer == data.endIndex { return readData }
while dataPointer != data.endIndex {
if data[dataPointer] == "<" {
break
} else {
readData.append(data[dataPointer])
dataPointer = data.index(after: dataPointer)
}
}
if dataPointer == data.endIndex {return readData }
if data[dataPointer] == "<" {
dataPointer = data.index(before: dataPointer)
}
return readData
}
func readGlobal(globalChunck:String)->globalData?{
let globaldata = globalData()
var samplestring = ""
var global = globalChunck
for part in globalChunck.components(separatedBy: .newlines){
if part.hasPrefix("sample") {
samplestring = part
}
}
if samplestring == "" {
//check for structure
if global.contains("sample") {
//get it out
var pointer = global.startIndex
var offset = global.index(pointer, offsetBy: 6, limitedBy: global.endIndex)
var s = ""
while offset != global.endIndex {
s = String(global[pointer..<offset!])
if s.contains("sample") {break}
pointer = global.index(after: pointer)
offset = global.index(pointer, offsetBy: 6, limitedBy: global.endIndex)
}
if s.contains("sample") {
//read to end
samplestring = String(global[pointer..<global.endIndex])
}
}
}
if samplestring != "" {
globaldata.sample = samplestring.components(separatedBy: "sample=")[1].replacingOccurrences(of: "\\", with: "/")
global = global.replacingOccurrences(of: samplestring, with: "", options: NSString.CompareOptions.literal, range: nil)
}
for part in global.components(separatedBy: .newlines) {
if part.hasPrefix("lovel") {
globaldata.lovel = Int32(part.components(separatedBy: "=")[1])!
} else if part.hasPrefix("hivel") {
globaldata.hivel = Int32(part.components(separatedBy: "=")[1])!
} else if part.hasPrefix("sample") {
globaldata.sample = part.components(separatedBy: "sample=")[1].replacingOccurrences(of: "\\", with: "/")
}
}
return globaldata
}
func readGroup(groupChunck:String)->groupData?{
let groupdata = groupData()
var samplestring = ""
var group = groupChunck
for part in groupChunck.components(separatedBy: .newlines){
if part.hasPrefix("sample") {
samplestring = part
}
}
if samplestring == "" {
//check for structure
if group.contains("sample") {
//get it out
var pointer = group.startIndex
var offset = group.index(pointer, offsetBy: 6, limitedBy: group.endIndex)
var s = ""
while offset != group.endIndex {
s = String(group[pointer..<offset!])
if s.contains("sample") {break}
pointer = group.index(after: pointer)
offset = group.index(pointer, offsetBy: 6, limitedBy: group.endIndex)
}
if s.contains("sample") {
//read to end
samplestring = String(group[pointer..<group.endIndex])
}
}
}
if samplestring != "" {
groupdata.sample = samplestring.components(separatedBy: "sample=")[1].replacingOccurrences(of: "\\", with: "/")
group = group.replacingOccurrences(of: samplestring, with: "", options: NSString.CompareOptions.literal, range: nil)
}
for part in group.components(separatedBy: .whitespacesAndNewlines) {
if part.hasPrefix("lovel") {
groupdata.lovel = Int32(part.components(separatedBy: "=")[1])!
} else if part.hasPrefix("hivel") {
groupdata.hivel = Int32(part.components(separatedBy: "=")[1])!
} else if part.hasPrefix("lokey") {
groupdata.lokey = Int32(part.components(separatedBy: "=")[1])!
} else if part.hasPrefix("hikey") {
groupdata.hikey = Int32(part.components(separatedBy: "=")[1])!
} else if part.hasPrefix("pitch_keycenter") {
groupdata.pitch = Int32(part.components(separatedBy: "=")[1])!
}else if part.hasPrefix("loop_mode") {
groupdata.loopmode = part.components(separatedBy: "=")[1]
}
}
return groupdata
}
func readRegion(regionChunck:String)->regionData{
let regiondata = regionData()
var samplestring = ""
var region = regionChunck
for part in regionChunck.components(separatedBy: .newlines){
if part.hasPrefix("sample") {
samplestring = part
}
}
// this for formats in wich ther are no newlines between region elements
if samplestring == "" {
//check for structure
if region.contains("sample") {
//get it out
var pointer = region.startIndex
var offset = region.index(pointer, offsetBy: 6, limitedBy: region.endIndex)
var s = ""
while offset != region.endIndex {
s = String(region[pointer..<offset!])
if s.contains("sample") {break}
pointer = region.index(after: pointer)
offset = region.index(pointer, offsetBy: 6, limitedBy: region.endIndex)
}
if s.contains("sample") {
//read to end
samplestring = String(region[pointer..<region.endIndex])
}
}
}
if samplestring != "" {
regiondata.sample = samplestring.components(separatedBy: "sample=")[1].replacingOccurrences(of: "\\", with: "/")
region = region.replacingOccurrences(of: samplestring, with: "", options: NSString.CompareOptions.literal, range: nil)
}
for part in region.components(separatedBy: .whitespacesAndNewlines) {
if part.hasPrefix("lovel") {
regiondata.lovel = Int32(part.components(separatedBy: "=")[1])!
} else if part.hasPrefix("hivel") {
regiondata.hivel = Int32(part.components(separatedBy: "=")[1])!
} else if part.hasPrefix("key=") {
regiondata.pitch = Int32(part.components(separatedBy: "=")[1])!
regiondata.lokey = regiondata.pitch
regiondata.hikey = regiondata.pitch
}else if part.hasPrefix("transpose") {
regiondata.transpose = Int32(part.components(separatedBy: "=")[1])!
} else if part.hasPrefix("tune") {
regiondata.tune = Int32(part.components(separatedBy: "=")[1])!
} else if part.hasPrefix("lokey") { // sometimes on one line
regiondata.lokey = Int32(part.components(separatedBy: "=")[1])!
} else if part.hasPrefix("hikey") {
regiondata.hikey = Int32(part.components(separatedBy: "=")[1])!
} else if part.hasPrefix("pitch_keycenter") {
regiondata.pitch = Int32(part.components(separatedBy: "=")[1])!
} else if part.hasPrefix("loop_mode") {
regiondata.loopmode = part.components(separatedBy: "=")[1]
} else if part.hasPrefix("loop_start") {
regiondata.loopstart = Float32(part.components(separatedBy: "=")[1])!
} else if part.hasPrefix("loop_end") {
regiondata.loopend = Float32(part.components(separatedBy: "=")[1])!
}else if part.hasPrefix("offset") {
regiondata.startPoint = Float32(part.components(separatedBy: "=")[1])!
}
else if part.hasPrefix("end") {
regiondata.endPoint = Float32(part.components(separatedBy: "=")[1])!
}
}
return regiondata
}
}
extension for sampler:
func loadSFZData(sfz:SFZ){
stopAllVoices()
unloadAllSamples()
for group in sfz.group {
for region in (group?.regions)! {
var sd = AKSampleDescriptor()
if region.pitch >= 0 {
sd.noteNumber = region.pitch
} else {
sd.noteNumber = (group?.pitch)!
}
var diff = Float(1)
if region.tune != 0 {
let fact = Float(pow(1.000578,Double(region.tune.magnitude)))
if region.tune < 0 {
diff *= fact
} else {
diff /= fact
}
}
sd.noteFrequency = Float(AKPolyphonicNode.tuningTable.frequency(forNoteNumber: MIDINoteNumber(sd.noteNumber-region.transpose)))*diff
if region.lovel >= 0 {
sd.minimumVelocity = region.lovel
} else {
sd.minimumVelocity = (group?.lovel)!
}
if region.hivel >= 0 {
sd.maximumVelocity = region.hivel
} else {
sd.maximumVelocity = (group?.hivel)!
}
if region.lokey >= 0 {
sd.minimumNoteNumber = region.lokey
} else {
sd.minimumNoteNumber = (group?.lokey)!
}
if region.hikey >= 0{
sd.maximumNoteNumber = region.hikey
} else {
sd.maximumNoteNumber = (group?.hikey)!
}
sd.loopStartPoint = region.loopstart
sd.loopEndPoint = region.loopend
var loopMode = ""
if region.loopmode != "" {
loopMode = region.loopmode
} else if group?.loopmode != "" {
loopMode = (group?.loopmode)!
}
sd.isLooping = loopMode != "" && loopMode != "no_loop"
sd.startPoint = region.startPoint
sd.endPoint = region.endPoint
// build sampldescriptor from region
// now sample
var sample = region.sample
if sample == "" { sample = (group?.sample)! }
if sample == "" { sample = sfz.global.sample}
if sample != "" {
let sampleFileURL = sfz.baseURL.appendingPathComponent(sample)
if sample.hasSuffix(".wv") {
loadCompressedSampleFile(from: AKSampleFileDescriptor(sampleDescriptor: sd, path: sampleFileURL.path))
} else {
if sample.hasSuffix(".aif") || sample.hasSuffix(".wav") {
let compressedFileURL = sfz.baseURL.appendingPathComponent(String(sample.dropLast(4) + ".wv"))
let fileMgr = FileManager.default
if fileMgr.fileExists(atPath: compressedFileURL.path) {
loadCompressedSampleFile(from: AKSampleFileDescriptor(sampleDescriptor: sd, path: compressedFileURL.path))
} else {
do {
let sampleFile = try AKAudioFile(forReading: sampleFileURL)
loadAKAudioFile(from: sd, file: sampleFile)
} catch {
debugPrint("error loading audiofile")
}
}
}
}
} //if sample
} //region
} //group
buildKeyMap()
restartVoices()
}
I am trying to save in coreData an object list on moveRowAt. I am saving my data in a singleton and it is well ordered but when I update on Core Data it's never saved and the order is wrong.
func changeKrakenPosition(source: Int, destiny: Int){
let context = appDelegate.persistentContainer.viewContext
do {
var results = try context.fetch(fetchRequest)
var source = source
var destiny = destiny
var destinyItem = results[destiny]
var sourceItem = results[source]
if source < destiny {
var dif = destiny - source
for i in 0...dif {
if i != dif {
var krakenCoreDataToMove = results[source+1]
results[source] = krakenCoreDataToMove
source += 1
} else {
results[source] = destinyItem
}
}
results[destiny] = sourceItem
} else if destiny < source {
var dif = source - destiny
for i in 0...dif {
if i != dif{
var krakenCoreDataToMove = results[source-1]
results[source] = krakenCoreDataToMove
source -= 1
} else {
results[source] = destinyItem
}
}
results[destiny] = sourceItem
} else {
//Moved to same position
}
var resultKra = results as! [Kraken]
for res in resultKra {
print("name: \(res.name) age \(res.age)")
}
do {
try context.save()
}catch{
//Handle Error
}
} catch {
print("Error with request: \(error)")
}
}
for the first time it works well but never saved, i enter again and I move the new first item to the last position and this is what happens
saving in CoreData i think would solve the problem but any help or solution would be great.
I Don't know exactly what do you want to do, but by the way, you can order your array easily with.
struct CustomObject{
let i: Int
let j: String
}
var array: [CustomObject] = [
CustomObject(i: 1, j: "1"),
CustomObject(i: 0, j: "0"),
CustomObject(i: 3, j: "3"),
]
let newArray = array.sorted { (first, second) -> Bool in
first.i < second.i
}
Here's what I am trying to do :
let courseName = "Bachelor of Tourism Administration(B.T.A)".condensedWhitespace
let upperCaseCourseName = courseName.uppercaseString
let extrctCourseName = upperCaseCourseName.componentsSeparatedByString(" ").reduce("") { $0.0 + String($0.1.characters.first!) }
let upperCasecourseFirstCharcters = extrctCourseName
print(upperCasecourseFirstCharcters) // output : "BOTA" but i want "BTA"
as you see that my outPut of "Bachelor of Tourism Administration(B.T.A)" is BOTA but the desired output is BTA because word of is starting from a lowerCase and i want to ignore that word in my this method , how am gonna do that any idea ?
let courseName = "Bachelor of Tourism Administration(B.T.A)" //.condensedWhitespace
var newString = ""
let array : NSArray = courseName.componentsSeparatedByString(" ")
for chr in array {
let str = chr as! NSString
if str.lowercaseString != str{
if newString.characters.count > 0{
newString = newString.stringByAppendingString(" "+(str as String))
continue
}
newString = newString.stringByAppendingString((str as String))
}
}
let upperCaseCourseName = newString.uppercaseString
let extrctCourseName = upperCaseCourseName.componentsSeparatedByString(" ").reduce("") { $0.0 + String($0.1.characters.first!) }
let upperCasecourseFirstCharcters = extrctCourseName
print(upperCasecourseFirstCharcters)
//This will defiantly meet to your problem/. Let me know if it works for u or not
You can paste this into a playground:
extension String {
func array() -> [String] {
return self.componentsSeparatedByString(" ")
}
func abbreviate() -> String {
var output = ""
let array = self.array()
for word in array {
let index = word.startIndex.advancedBy(0)
let str = String(word[index])
if str.lowercaseString != str {
output += str
}
}
return output
}
}
let courseName = "Bachelor of Tourism Administration(B.T.A)".abbreviate()
print(courseName) // prints BTA
A clean approach would be:
extension Character
{
public func isUpper() -> Bool
{
let characterString = String(self)
return (characterString == characterString.uppercaseString) && (characterString != characterString.lowercaseString)
}
}
let courseName = "Bachelor of Tourism Administration(B.T.A)"
let upperCaseCourseName = courseName
let extrctCourseName = upperCaseCourseName.componentsSeparatedByString(" ").reduce("") {
if($0.1.characters.first!.isUpper()) {
return $0.0 + String($0.1.characters.first!)
}else {
return $0.0
}
}
I have a array that looks like this:
var myArray = ["1one", "1two", "1three", "1four", "1five", "1six"]
And to get random, I use this:
var originalNames = [String]()
func getRandomName() -> String {
if (names.count == 0) {
names = originalNames
}
let randomNumber = Int(arc4random_uniform(UInt32(names.count)))
return names.removeAtIndex(randomNumber)
}
And I use it like this:
self.randomLabel.text = getRandomName()
As you can see, the array contains six different strings. The code that I am currently using, will return add the strings inside the array at random, but I only want to return the first 3 strings randomly. How can I do this?
You can try using
var originalNames = [String]()
func getRandomName() -> String {
if (names.count == 0) {
names = originalNames
}
let randomNumber = Int(arc4random_uniform(3))
return names[randomNumber]
}
so let randomNumber = Int(arc4random_uniform(3)) this will return random Int value upto 3.
var myArray = ["1one", "1two", "1three", "1four", "1five", "1six"]
var result:[String] = []
while result.count < 3 {
let randomNumber = Int(arc4random_uniform(UInt32(myArray.count)))
result.append(myArray.removeAtIndex(randomNumber))
}
print(result) // "["1two", "1one", "1three"]\n"
if you don't want to modify the original array just make a copy of it
let myArray = ["1one", "1two", "1three", "1four", "1five", "1six"]
var inputNames = myArray
var result:[String] = []
while result.count < 3 {
result.append(inputNames.removeAtIndex(Int(arc4random_uniform(UInt32(inputNames.count)))))
}
print(result) // "["1six", "1two", "1one"]\n"
I am creating an EPUB 3 reader for iOS using Swift 2.
The problem I'm currently facing is with font obfuscation / font mangling. I've read a tutorial that goes over how to do that in Swift, and integrated it into my project with some adaptations.
When I load an obfuscated epub into my app, the fonts are not loaded correctly and fall back to other system fonts. When I load an epub with the same fonts but not obfuscated, everything looks fine. Obviously, that means there's something wrong with my obfuscation code, but I can't for the life of me find the error.
Here's my code:
public struct Crypto {
public func obfuscateFontIDPF(data:NSData, key:String) -> NSData {
let source = data
var destination = [UInt8]()
let shaKey = key.sha1()
let keyData = shaKey.utf8Array
var arr = [UInt8](count: source.length, repeatedValue: 0)
source.getBytes(&arr, length:source.length)
var outer = 0
while outer < 52 && arr.isEmpty == false {
var inner = 0
while inner < 20 && arr.isEmpty == false {
let byte = arr.removeAtIndex(0) //Assumes read advances file position
let sourceByte = byte
let keyByte = keyData[inner]
let obfuscatedByte = sourceByte ^ keyByte
destination.append(obfuscatedByte)
inner++
}
outer++
}
if arr.isEmpty == false {
while arr.isEmpty == false {
let byte = arr.removeAtIndex(0)
destination.append(byte)
}
}
let newData = NSData(bytes: &destination, length: destination.count*sizeof(UInt8))
return newData
}
}
extension String {
func sha1() -> String {
var selfAsSha1 = ""
if let data = self.dataUsingEncoding(NSUTF8StringEncoding)
{
var digest = [UInt8](count: Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA1(data.bytes, CC_LONG(data.length), &digest)
for index in 0..<CC_SHA1_DIGEST_LENGTH
{
selfAsSha1 += String(format: "%02x", digest[Int(index)])
}
}
return selfAsSha1
}
var utf8Array: [UInt8] {
return Array(utf8)
}
}
And here I call the obfuscation method:
func parserDidEndDocument(parser: NSXMLParser) {
if encryptedFilePaths!.count != 0 {
for file in encryptedFilePaths! {
let epubMainDirectoryPath = NSString(string: epubBook!.epubMainFolderPath!).stringByDeletingLastPathComponent
let fullFilePath = epubMainDirectoryPath.stringByAppendingString("/" + file)
let url = NSURL(fileURLWithPath: fullFilePath)
if let source = NSData(contentsOfURL: url) {
let decryptedFont = Crypto().obfuscateFontIDPF(source, key: self.epubBook!.encryptionKey!)
do {
try decryptedFont.writeToFile(fullFilePath, options: .DataWritingAtomic)
} catch {
print(error)
}
}
}
}
}
If you see where the error might be, please let me know.
I figured it out, here is the working code:
private func obfuscateData(data: NSData, key: String) -> NSData {
var destinationBytes = [UInt8]()
// Key needs to be SHA1 hash with length of exactly 20 chars
let hashedKeyBytes = generateHashedBytesFromString(key)
var sourceBytes = [UInt8](count: data.length, repeatedValue: 0)
data.getBytes(&sourceBytes, length: data.length)
var outerCount = 0
while outerCount < 52 && sourceBytes.isEmpty == false {
var innerCount = 0
while innerCount < 20 && sourceBytes.isEmpty == false {
let sourceByte = sourceBytes.removeAtIndex(0)
let keyByte = hashedKeyBytes[innerCount]
let obfuscatedByte = (sourceByte ^ keyByte)
destinationBytes.append(obfuscatedByte)
innerCount += 1
}
outerCount += 1
}
destinationBytes.appendContentsOf(sourceBytes)
let destinationData = NSData(bytes: &destinationBytes, length: destinationBytes.count*sizeof(UInt8))
sourceBytes.removeAll(keepCapacity: false)
destinationBytes.removeAll(keepCapacity: false)
return destinationData
}
/// Convert the key string to a SHA1 hashed Byte Array
private func generateHashedBytesFromString(string: String) -> [UInt8] {
var resultBytes = [UInt8]()
var hashedString = string.sha1()
for _ in 0.stride(to: hashedString.characters.count, by: 2) {
let character = "0x\(hashedString.returnTwoCharacters())"
resultBytes.append(UInt8(strtod(character, nil)))
}
return resultBytes
}
extension String {
func sha1() -> String {
var selfAsSha1 = ""
if let data = self.dataUsingEncoding(NSUTF8StringEncoding) {
var digest = [UInt8](count: Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA1(data.bytes, CC_LONG(data.length), &digest)
for index in 0..<CC_SHA1_DIGEST_LENGTH {
selfAsSha1 += String(format: "%02x", digest[Int(index)])
}
}
return selfAsSha1
}
mutating func returnTwoCharacters() -> String {
var characters: String = ""
characters.append(self.removeAtIndex(startIndex))
characters.append(self.removeAtIndex(startIndex))
return characters
}
}