NSCache not thread safe in swift code? - ios

I'm not sure if this thread unsafe in Swift,Output should be 31 characters, is such that NSCache isn't thread safe?
let nscache = NSCache()
nscache.setObject("", forKey: "1")
let globalQueue = dispatch_get_global_queue(0, 0)
dispatch_async(globalQueue) {
print(NSThread.currentThread())
for i in 0...10 {
let str = nscache.objectForKey("1") as! String + ".\(i)"
nscache.setObject(str, forKey: "1")
print("str:\(str)")
}
}
dispatch_async(globalQueue) {
print(NSThread.currentThread())
for i in 11...20 {
let str = nscache.objectForKey("1") as! String + ".\(i)"
nscache.setObject(str, forKey: "1")
print("str:\(str)")
}
}
dispatch_async(globalQueue) {
print(NSThread.currentThread())
for i in 21...30 {
let str = nscache.objectForKey("1") as! String + ".\(i)"
nscache.setObject(str, forKey: "1")
print("str:\(str)")
}
}
Output
/////////////////print/////////////////
str:.21 str:.21.11 str:.21.11.0 str:.21.11.0.22 str:.21.11.0.22.12
str:.21.11.0.22.12.1 str:.21.11.0.22.12.1.23
str:.21.11.0.22.12.1.23.13 str:.21.11.0.22.12.1.23.13.24
str:.21.11.0.22.12.1.23.13.2 str:.21.11.0.22.12.1.23.13.2.14
str:.21.11.0.22.12.1.23.13.2.25 str:.21.11.0.22.12.1.23.13.2.3
str:.21.11.0.22.12.1.23.13.2.3.15 str:.21.11.0.22.12.1.23.13.2.3.15.26
str:.21.11.0.22.12.1.23.13.2.3.15.26.4
str:.21.11.0.22.12.1.23.13.2.3.15.26.4.16
str:.21.11.0.22.12.1.23.13.2.3.15.26.4.16.5
str:.21.11.0.22.12.1.23.13.2.3.15.26.4.16.27
str:.21.11.0.22.12.1.23.13.2.3.15.26.4.16.27.17
str:.21.11.0.22.12.1.23.13.2.3.15.26.4.16.27.17.6
str:.21.11.0.22.12.1.23.13.2.3.15.26.4.16.27.17.6.28
str:.21.11.0.22.12.1.23.13.2.3.15.26.4.16.27.17.6.28.18
str:.21.11.0.22.12.1.23.13.2.3.15.26.4.16.27.17.6.28.18.29
str:.21.11.0.22.12.1.23.13.2.3.15.26.4.16.27.17.6.28.18.7
str:.21.11.0.22.12.1.23.13.2.3.15.26.4.16.27.17.6.28.18.7.19
str:.21.11.0.22.12.1.23.13.2.3.15.26.4.16.27.17.6.28.18.7.19.30
str:.21.11.0.22.12.1.23.13.2.3.15.26.4.16.27.17.6.28.18.7.19.30.8
str:.21.11.0.22.12.1.23.13.2.3.15.26.4.16.27.17.6.28.18.7.19.30.8.20
str:.21.11.0.22.12.1.23.13.2.3.15.26.4.16.27.17.6.28.18.7.19.30.8.20.9
str:.21.11.0.22.12.1.23.13.2.3.15.26.4.16.27.17.6.28.18.7.19.30.8.20.9.10

Related

CoreData fetch - memory leaks

When I try to catch data from CoreData I get a memory leak.
My function for fetching is:
func fetchTableBodyData<T: TableBody, C: NSManagedObject>(from year: Int?, coreDataObject: C.Type, returnType: T.Type) -> [T] {
guard let name = C.entity().name else { return [] }
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: name)
fetchRequest.returnsObjectsAsFaults = false
if let year = year {
fetchRequest.predicate = NSPredicate(format: "date >= %# AND date <= %#", Date().startOfYear(year: year) as CVarArg, Date().endOfYear(year: year) as CVarArg)
}
do {
let fetched = try context.fetch(fetchRequest) // Memory leak is here
guard let casted = fetched as? [C] else { return [] }
return T.parseFromCoreData(from: casted)
} catch let er {
print(er.localizedDescription)
return []
}
}
Context is define:
private var context: NSManagedObjectContext {
CoreDataStack.managedObjectContext
}
I have another memory leak in parsing from CoreDataObject to Struct:
static func getYearlyDataFrom(object: YearObject) -> YearlyData? {
guard let objects = object.tableObjects else { return nil } // Memory leak
var yearlyData: YearlyData = YearlyData()
yearlyData.year = Int(object.year)
var tableDatas: [TableData] = []
for tableObject in objects { // Memory leak
guard let tableObj = tableObject as? TableObject else { continue }
guard let id = tableObj.id, let nameOfList = tableObj.nameOfList, let range = tableObj.nameOfList, let lastUpdate = tableObj.lastUpdate else { continue } // Memory leak
tableDatas.append(TableData(id: id, nameOfList: nameOfList, range: range, lastUpdate: lastUpdate))
}
var playlists: [Playlist] = []
for playlistObject in object.playlistObjects ?? NSSet() { // Memory leak
guard let playlistObj = playlistObject as? PlaylistObject else { continue }
guard let playlistId = playlistObj.playlistId, let date = playlistObj.date, let name = playlistObj.name, let imageURL = playlistObj.imageURL else { continue } // Memory leak
var image: UIImage? = nil
if let imageData = playlistObj.image {
image = UIImage(data: imageData)
}
playlists.append(Playlist(playlistId: playlistId, name: name, position: Int(playlistObj.position), date: date, imageURL: imageURL, image: image))
}
yearlyData.playlists = playlists
yearlyData.tables = tableDatas
return yearlyData
}
Did anybody have the same problem?
The context object is defined as a Computed-Property it should be a State-Property instead and shouldn't be computed each time it's called.
private var context = CoreDataStack.managedObjectContext

Crashed: com.apple.main-thread EXC_BREAKPOINT 0x0000000100c5009c keyboard_arrow_up

I am new iOS development, I got this error from firebase crash analytics. Can anyone help me why this errors occurs?
Crashed: com.apple.main-thread
0 People Time Tracking 0x100c5009c closure #1 in closure #1 in SelectJobScreen.getPreviousStatus() (SelectJobScreen.swift:798)
1 People Time Tracking 0x100cb2648 thunk for #escaping #callee_guaranteed () -> () (<compiler-generated>)
2 libdispatch.dylib 0x1e6993a38 _dispatch_call_block_and_release + 24
3 libdispatch.dylib 0x1e69947d4 _dispatch_client_callout + 16
4 libdispatch.dylib 0x1e6942004 _dispatch_main_queue_callback_4CF$VARIANT$mp + 1068
5 CoreFoundation 0x1e6ee4ec0 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
6 CoreFoundation 0x1e6edfdf8 __CFRunLoopRun + 1924
7 CoreFoundation 0x1e6edf354 CFRunLoopRunSpecific + 436
8 GraphicsServices 0x1e90df79c GSEventRunModal + 104
9 UIKitCore 0x212cc5b68 UIApplicationMain + 212
10 People Time Tracking 0x100c1afcc main (TodoItem.swift:17)
11 libdyld.dylib 0x1e69a58e0 start + 4
I've tried figuring out what could cause this crash for a few days now, and haven't been able to reproduce it. I don't see any implicit unwraps or optionals here, but sessionId is a non-optional value in the session object if that matters.
I am using swift 4.1, and the crashes occur on on iOS devices running all the different flavours of iOS 10, 11, and 12. The app does support some builds of iOS 9, but none have been reported (although that may be irrelevant because the iOS 9 user base for the app is extremely small)
In Error log it's showing this function getPreviousStatus()
func getPreviousStatus() {
let connect = JsonManger()
let app = UIApplication.shared.delegate as! AppDelegate
let user:User = app.dataManager.gUser
if Reachability.isConnectedToNetwork() {
self.showHUD( msg: "Loading...." )
connect.getLogDetails(baseUrl: user.ClientWeb, email: user.EmailID, password: user.Password, brugerId: user.BrugerID, success: { (res) in
var indud = false
if ( res.object(forKey: "indud") != nil ){
indud = res.object(forKey: "indud") as! Bool
}
if indud {
var isTodo = false
if res.object(forKey: "isToDo") != nil {
isTodo = res.object(forKey: "isToDo") as! Bool
}
if isTodo {
var str = res.object(forKey: "JobId") as! String
let index = str.index(str.startIndex, offsetBy: 1)
str = str.substring(from: index)
let prjId = Int(str)!
let project = app.dataManager.gProject.getProject(id: prjId)
let todoId = res.object(forKey: "TodoId") as! String
let todo = app.dataManager.gTodo.getTodo(id: Int(todoId)!)
self.project = project
self.todoItem = todo
app.dataManager.isCheckedin = true
self.des = res.object(forKey: "JobDescription") as? String
let startTime = res.object(forKey: "Dato2") as! String
DispatchQueue.main.async {
let ud = UserDefaults.standard
ud.set(true, forKey: "isCheckedIn")
ud.set( 1, forKey:"type")
ud.set( self.project.ID!, forKey: "projectId" )
ud.set( self.todoItem.ID! , forKey: "todoId" )
// print(" todo data is : \(self.todoItem.ID!)")
ud.set( self.des, forKey: "des")
ud.set( startTime,forKey: "startTime")
ud.set( startTime, forKey: "checkInTime" )
ud.synchronize()
self.hideHUD()
self.performSegue(withIdentifier: "goStartTodoFromSelect", sender: nil )
}
} else {
var jobId = res.object(forKey: "JobId") as! String
jobId = jobId.replacingOccurrences(of: "P", with: "")
self.des = res.object(forKey: "JobDescription") as? String
let jobCode = app.dataManager.gJobCode.getJobCode(id: Int(jobId)! )
self.jobCodeItem = jobCode
// let chekinTime = res.object(forKey: "Dato2") as! String
let startTime = "2019-05-15T19:45:00"
DispatchQueue.main.async {
app.dataManager.isCheckedin = true
let ud = UserDefaults.standard
ud.set(true, forKey: "isCheckedIn")
ud.set( 0, forKey:"type")
ud.set( jobId, forKey: "jobId" )
ud.set( startTime,forKey: "startTime")
ud.set( startTime, forKey: "checkInTime" )
if res.object(forKey: "JobDescription") != nil {
self.des = res.object(forKey: "JobDescription") as? String
ud.set( self.des, forKey: "des")
}
ud.synchronize()
DispatchQueue.main.async {
self.hideHUD()
self.performSegue(withIdentifier: "goStartJob", sender: nil )
}
}
}
} else {
DispatchQueue.main.async {
self.hideHUD()
// self.popToRoot()
for controller in self.navigationController!.viewControllers as Array {
if controller.isKind(of: SelectJobScreen.self) {
self.navigationController!.popToViewController(controller, animated: true)
break
}
}
}
}
}) { (error) in
DispatchQueue.main.async {
self.hideHUD()
// self.popToRoot()
for controller in self.navigationController!.viewControllers as Array {
if controller.isKind(of: SelectJobScreen.self) {
self.navigationController!.popToViewController(controller, animated: true)
break
}
}
}
}
} else {
print("Record Not Found")
}
}
Inside of that getPreviousStatus() functions getting null values , check it once using debugging
DispatchQueue.main.async {
# mainly in this thread getting error check it once
let ud = UserDefaults.standard
ud.set(true, forKey: "isCheckedIn")
ud.set( 1, forKey:"type")
ud.set( self.project.ID!, forKey: "projectId" )
ud.set( self.todoItem.ID! , forKey: "todoId" )
// print(" todo data is : \(self.todoItem.ID!)")
ud.set( self.des, forKey: "des")
ud.set( startTime,forKey: "startTime")
ud.set( startTime, forKey: "checkInTime" )
ud.synchronize()
self.hideHUD()
self.performSegue(withIdentifier: "goStartTodoFromSelect", sender: nil )
}

Taking so much time when i insert 10k data in coredata ?

In my project concept i need a insert 10k data when user open the application. I integrate core data for storing data but its take 1 to 5 minutes.
Here is my code ?
func inserChatMessage(_ message: String, chatId: String, onCompletion completionHandler:((_ message: ChatMessage) -> Void)?) {
var objMessage: ChatMessage? = nil
if let obj = ChatMessage.createEntity() {
objMessage = obj
}
objMessage?.messageId = ""
objMessage?.message = message
objMessage?.chatId = chatId
objMessage?.senderId = AIUser.current.userId
objMessage?.createAt = Date()
objMessage?.updateAt = Date()
let cManager = CoreDataManager.sharedManager
cManager.saveContext()
if let completionHandler = completionHandler, let objMessage = objMessage {
completionHandler(objMessage)
}
}
Coredata is not a threadsafe. And as per your requirement you need to save large amount of data on app launch. So If you will save those data using main thread, your app will get hanged. So Instead on saving large amount of data on main thread you can save those data on background thread. Coredata is supporting multi threading concept by providing parent child context concept.
I have done same in one of my project and its working fine. Here i have attached code.
func savePersonalMessagesOnBackGroundThread(arrMessages:NSArray,responseData:#escaping () -> Void)
{
print(arrMessages)
let temporaryChatContext = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.privateQueueConcurrencyType)
temporaryChatContext.parent = self.managedObjectContext
temporaryChatContext.perform({() -> Void in
for i in 0..<arrMessages.count
{
let msgDic = arrMessages[i] as! NSDictionary
_ = self.saveMessageInLocalDB(dictMessage: msgDic, managedObjectContext: temporaryChatContext, onBackground: true)
if i == arrMessages.count - 1 {
do {
try temporaryChatContext.save()
runOnMainThreadWithoutDeadlock {
DLog(message: "Thred \(Thread.isMainThread)")
if(self.managedObjectContext.hasChanges)
{
self.saveContext()
responseData()
}
}
}
catch {
print(error)
}
}
}
})
}
func saveMessageInLocalDB(dictMessage:NSDictionary, managedObjectContext:NSManagedObjectContext,onBackground:Bool) -> Chat
{
var chatObj : Chat! = Chat()
var receiveId: Int32!
var flag:Bool = false
print(dictMessage)
// let predicate = NSPredicate(format:"uniqueId == %# and senderId = %d and receiverId = %d","\(dictMessage.value(forKey:keyuniqueId)!)",Int32(dictMessage.value(forKey:keysenderId) as! Int64),Int32(dictMessage.value(forKey:keyreceiverId) as! Int64))
let predicate = NSPredicate(format:"uniqueId == %#","\(dictMessage.value(forKey:keyuniqueId)!)")
let objContext = managedObjectContext
let fetchRequest = NSFetchRequest<Chat>(entityName: ENTITY_CHAT)
let disentity: NSEntityDescription = NSEntityDescription.entity(forEntityName: ENTITY_CHAT, in: objContext)!
fetchRequest.predicate = predicate
fetchRequest.entity = disentity
do{
let results = try managedObjectContext.fetch(fetchRequest as! NSFetchRequest<NSFetchRequestResult>) as! [Chat]
if(results.count > 0)
{
chatObj = results[0]
chatObj.messageId = Int32(dictMessage.value(forKey:keymessageId) as! Int64)
chatObj.dateOnly = dictMessage.value(forKey:keydateOnly) as! String?
}
else{
//receiveId = Int32(dictMessage.value(forKey:keyreceiverId) as! Int64)
//self.createNewChatObject(dictMessage: dictMessage, receiverId: receiveId, managedObjectContext: managedObjectContext)
chatObj = NSEntityDescription.insertNewObject(forEntityName:ENTITY_CHAT,into: managedObjectContext) as? Chat
if dictMessage[keymessageId] != nil {
chatObj.messageId = dictMessage.value(forKey:keymessageId) as! Int32
}
if(chatObj.message?.length != 0)
{
chatObj.message = dictMessage.value(forKey:keychatMessage) as? String
}
chatObj.messageType = Int32(dictMessage.value(forKey:keymessageType) as! Int64)
chatObj.senderId = Int32(dictMessage.value(forKey:keysenderId) as! Int64)
if(chatObj.senderId != Int32((APP_DELEGATE.loggedInUser?.id!)!))
{
let contactObj = self.getContactByContactId(contactId: Int32(dictMessage.value(forKey:keysenderId) as! Int64))
if(contactObj == nil)
{
_ = self.saveUnknownUserASContact(msgDict: dictMessage as! Dictionary<String, Any>)
}
}
chatObj.receiverId = Int32(dictMessage.value(forKey:keyreceiverId) as! Int64)
chatObj.uniqueId = dictMessage.value(forKey:keyuniqueId) as? String
chatObj.mediaName = dictMessage.value(forKey:keymediaName) as? String
print(NSDate())
if dictMessage[keycreatedDate] != nil {
let utcDate : NSDate = DateFormater.getUTCDateFromUTCString(givenDate: dictMessage.value(forKey:keycreatedDate) as! String)
chatObj.createdDate = utcDate
chatObj.updatedDate = utcDate
}
else
{
chatObj.createdDate = NSDate()
chatObj.updatedDate = NSDate()
}
if(chatObj.senderId == Int32((APP_DELEGATE.loggedInUser?.id)!))
{
chatObj.chatUser = chatObj.receiverId
}
else
{
chatObj.chatUser = chatObj.senderId
}
if dictMessage[keystatus] != nil {
chatObj.status = Bool((dictMessage.value(forKey:keystatus) as! Int64) as NSNumber)
}
switch Int(chatObj.messageType)
{
case MSG_TYPE.MSG_Text.rawValue:
chatObj.cellType = (chatObj.senderId != Int32((APP_DELEGATE.loggedInUser?.id!)!) ? Int32(CELL_TYPE.CELL_TEXT_RECEIVER.rawValue) : Int32(CELL_TYPE.CELL_TEXT_SENDER.rawValue))
case MSG_TYPE.MSG_Image.rawValue:
chatObj.cellType = (chatObj.senderId != Int32((APP_DELEGATE.loggedInUser?.id!)!) ? Int32(CELL_TYPE.CELL_IMAGE_RECEIVER.rawValue) : Int32(CELL_TYPE.CELL_IMAGE_SENDER.rawValue))
self.saveMedia(chatObj: chatObj)
default :
// chatObj.cellType = Int32(CELL_TYPE.CELL_LOAD_MORE.rawValue)
break
}
}
// deviceMake = 1;
if(!onBackground)
{
self.saveContext()
}
}
catch
{
}
return chatObj
}
Using the basic example below I can insert 10k records very quickly. The main thing that has changed here compared to your code is that I loop through and create the entities and then call save() at the very end. So you are performing one write call to the db instead of 10k. You are writing more information in that one call but it is still much quicker.
import UIKit
import CoreData
class ViewController: UIViewController {
lazy var sharedContext: NSManagedObjectContext? = {
return (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer.viewContext
}()
override func viewDidLoad() {
super.viewDidLoad()
if let messages = getMessages(), messages.count > 0 {
printMessages(messages: messages)
} else {
loadChatMessages()
printMessages(messages: getMessages())
}
}
private func printMessages(messages: [Message]?) {
guard let messages = messages else { return }
for message in messages {
print(message.message)
}
}
private func getMessages() -> [Message]? {
let request = NSFetchRequest<Message>(entityName: "Message")
let messages = try? self.sharedContext?.fetch(request)
return messages ?? nil
}
private func loadChatMessages() {
var counter = 1
while counter <= 10000 {
let message = Message(entity: Message.entity(), insertInto: self.sharedContext)
message.message = "This is message number \(counter)"
message.read = false
message.timestamp = Date()
counter = counter + 1
}
do {
try self.sharedContext?.save()
} catch {
print(error)
}
}
}
As mentioned in my comment above, you can improve this further by doing it in the background (see Twinkle's answer for an example of how to switch to a background thread), you can also provide a pre-filled (pre-seeded) core data database that already contains the 10k records with your app. so it doesn't need to load this on initial load.
To do this you would fill the db locally on your dev machine and then copy it to the project bundle. On initial load you can check to see if your db filename exists in the documents folder or not. If it doesn't copy it over from the bundle and then use that DB for core data.

I am trying to get an icon array to display in my weather app, but can not seem to get UIImage to display them

This is my code so far, with no errors, but it is not picking the dates from the 5 day forecast. What is wrong in this code?
//: to display the 5 day date array from the open weather API
enter code herevar temperatureArray: Array = Array()
var dayNumber = 0
var readingNumber = 0
if let jsonObj = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? NSDictionary {
if let mainArray = jsonObj!.value(forKey: "list") as? NSArray {
for dict in mainArray {
if let mainDictionary = (dict as! NSDictionary).value(forKey: "main") as? NSDictionary {
if let temperature = mainDictionary.value(forKey: "temp_max") as? Double {
if readingNumber == 0 {
temperatureArray.append(temperature)
} else if temperature > temperatureArray[dayNumber] {
temperatureArray[dayNumber] = temperature
}
} else {
print("Error: unable to find temperature in dictionary")
}
} else {
print("Error: unable to find main dictionary")
}
readingNumber += 1
if readingNumber == 8 {
readingNumber = 0
dayNumber += 1
}
var dateArray: Array<String> = Array()
var dayNumber = 0
var readingNumber = 0
if let weatherArray = jsonObj!.value(forKey: "list") as? NSArray {
for dict in weatherArray {
if let weatherDictionary = (dict as! NSDictionary).value(forKey: "list") as? NSDictionary {
if let date = weatherDictionary.value(forKey: "dt_txt") as? String {
if readingNumber == 0 {
dateArray.append(date)
} else if date > dateArray[dayNumber] {
dateArray[dayNumber] = date
}
}
} else {
print("Error: unable to find date in dictionary")
}
readingNumber += 1
if readingNumber == 8 {
readingNumber = 0
dayNumber += 1
}
}
}
}
}
}
func fixTempForDisplay(temp: Double) -> String {
let temperature = round(temp)
let temperatureString = String(format: "%.0f", temperature)
return temperatureString
}
DispatchQueue.main.async {
self.weatherLabel1.text = "Today: (fixTempForDisplay(temp: temperatureArray[0]))°C"
self.weatherLabel2.text = "Tomorrow: (fixTempForDisplay(temp: temperatureArray[1]))°C"
self.weatherLabel3.text = "Day 3: (fixTempForDisplay(temp: temperatureArray[2]))°C"
self.weatherLabel4.text = "Day 4: (fixTempForDisplay(temp: temperatureArray[3]))°C"
self.weatherLabel5.text = "Day 5: (fixTempForDisplay(temp: temperatureArray[4]))°C"
func formatDate(date: NSDate) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
return dateFormatter.string(from: date as Date)
}
self.dateLabel1.text = ": \(formatDate(date: dateArray[0]))"
self.dateLabel2.text = ": \(formatDate(date: dateArray[1]))"
self.dateLabel3.text = ": \(formatDate(date: dateArray[2]))"
self.dateLabel4.text = ": \(formatDate(date: dateArray[3]))"
self.dateLabel5.text = ": \(formatDate(date: dateArray[4]))"
}
}
}
dataTask.resume()
}
}
It looks to me like you need to change your icon array to contain strings
var iconArray: Array<String> = Array()
and then when parsing the json text
if let icon = weatherDictionary.value(forKey: "icon") as? String {
and finally
self.iconImage1.image = UIImage(named: iconArray[0])
self.iconImage2.image = UIImage(named: iconArray[1])
Of course the below comparison won't work anymore when icon is a string but I don't understand any of this if/else clasue so I don't know what to replace it with
if readingNumber == 0 {
iconArray.append(icon)
} else if icon > iconArray[dayNumber] { //This won't work now.
iconArray[dayNumber] = icon
}

Ignore a letter in swift which starts with a Lower Case

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
}
}

Resources