Check the valid phone number - ios

I have one text field for enter the phone number and user have to press OK button.
Then I write some function to check whether entered number is valid number or 10 digit number. And I don't want to add country code. That I have separately.
But when I press OK button its give me uialert - wrong number for all number including my own number. I don't know any code I missed?
func validate(value: String) -> Bool {
let PHONE_REGEX = "^\\d{3}-\\d{3}-\\d{4}$"
var phoneTest = NSPredicate(format: "SELF MATCHES %#", PHONE_REGEX)
var result = phoneTest.evaluateWithObject(value)
return result
}
#IBAction func confirmAction(sender: AnyObject) {
if validate(phoneNumber.text!)
{
print("Validate EmailID")
let phone = countryCode.text! + phoneNumber.text!
UserNetworkInterface().generateSms(phone, onCompletion: nil)
performSegueWithIdentifier("ConfirmSmsCode", sender: self)
}
else
{
print("invalide EmailID")
let alert = UIAlertView()
alert.title = "Message"
alert.message = "Enter Valid Contact Number"
alert.addButtonWithTitle("Ok")
alert.delegate = self
alert.show()
}
}
Updated :
#IBAction func confirmAction(sender: AnyObject) {
if let phoneNumberValidator = phoneNumber.isPhoneNumber
{
print("Validate EmailID")
let phone = countryCode.text! + phoneNumber.text!
UserNetworkInterface().generateSms(phone, onCompletion: nil)
performSegueWithIdentifier("ConfirmSmsCode", sender: self)
}
else
{
print("invalide EmailID")
let alert = UIAlertView()
alert.title = "Message"
alert.message = "Enter Valid Contact Number"
alert.addButtonWithTitle("Ok")
alert.delegate = self
alert.show()
phoneNumber.text = ""
}
// Number valid
}

Try this.
Make an extension to String.
Swift 4
extension String {
var isPhoneNumber: Bool {
do {
let detector = try NSDataDetector(types: NSTextCheckingResult.CheckingType.phoneNumber.rawValue)
let matches = detector.matches(in: self, options: [], range: NSRange(location: 0, length: self.count))
if let res = matches.first {
return res.resultType == .phoneNumber && res.range.location == 0 && res.range.length == self.count
} else {
return false
}
} catch {
return false
}
}
}
Older Swift Versions
extension String {
var isPhoneNumber: Bool {
do {
let detector = try NSDataDetector(types: NSTextCheckingType.PhoneNumber.rawValue)
let matches = detector.matchesInString(self, options: [], range: NSMakeRange(0, self.characters.count))
if let res = matches.first {
return res.resultType == .PhoneNumber && res.range.location == 0 && res.range.length == self.characters.count
} else {
return false
}
} catch {
return false
}
}
}
Usage:
override func viewWillAppear(animated: Bool) {
//Sample check
let phoneString = "8888888888"
let phoneNumberValidator = phoneString.isPhoneNumber
print(phoneNumberValidator)
}

Swift 3
For those of you who would like the phone number to have a minimum of 10 characters use the below code (Amended #Alvin George code)
extension String {
var isPhoneNumber: Bool {
do {
let detector = try NSDataDetector(types: NSTextCheckingResult.CheckingType.phoneNumber.rawValue)
let matches = detector.matches(in: self, options: [], range: NSMakeRange(0, self.characters.count))
if let res = matches.first {
return res.resultType == .phoneNumber && res.range.location == 0 && res.range.length == self.characters.count && self.characters.count == 10
} else {
return false
}
} catch {
return false
}
}
}
Usage
ooverride func viewDidLoad() {
super.viewDidLoad()
//Example
let phoneNumberString = "8500969696"
let phoneNumberValidation = phoneNumberString.isPhoneNumber
print(phoneNumberValidation)
// Prints: true
}

Try this for Indian 10 digit mobile number validation
var isValidMobileNo: Bool {
let PHONE_REGEX = "^[7-9][0-9]{9}$";
let phoneTest = NSPredicate(format: "SELF MATCHES %#", PHONE_REGEX)
let result = phoneTest.evaluate(with: self)
return result
}

Amended #Alvin George code for Swift 4 that also accepts only 10 digit phone numbers:
extension String {
var isPhoneNumber: Bool {
do {
let detector = try NSDataDetector(types: NSTextCheckingResult.CheckingType.phoneNumber.rawValue)
let matches = detector.matches(in: self, options: [], range: NSMakeRange(0, self.count))
if let res = matches.first {
return res.resultType == .phoneNumber && res.range.location == 0 && res.range.length == self.count && self.count == 10
} else {
return false
}
} catch {
return false
}
}
}

Easy validation for Indian 10 digit mobile number
func isValidPhone(testStr:String) -> Bool {
let phoneRegEx = "^[6-9]\\d{9}$"
var phoneNumber = NSPredicate(format:"SELF MATCHES %#", phoneRegEx)
return phoneNumber.evaluate(with: testStr)
}

Modifying #A.G's answer to update to Swift 5:
extension String {
func isValidPhone() -> Bool {
do {
let detector = try NSDataDetector(types: NSTextCheckingResult.CheckingType.phoneNumber.rawValue)
let matches = detector.matches(in: self, options: [], range: NSMakeRange(0, self.count))
if let res = matches.first {
return res.resultType == .phoneNumber && res.range.location == 0 && res.range.length == self.count
} else {
return false
}
} catch {
return false
}
}
}

Related

Issue with emojis in TextView in Swift

I’m using the below code to get the cursor position when the user types something in the textView.
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
inputChar = text
inputRange = range
inputIndex = inputRange?.upperBound
self.presenter.hashtagDataArray.removeAll()
if(text == "\n") {
textView.resignFirstResponder()
toggleTableView(toggle: true)
return false
}
let str = (textView.text + text)
if str.utf16.count <= MediaPostViewController.descCharacterLimits {
return true
}
let numberOfChars = str.utf16.count
lbbbl_DescCount.text = "\(textView.text.utf16.count)/\(MediaPostViewController.descCharacterLimits)"
return (numberOfChars <= MediaPostViewController.descCharacterLimits) || (str.utf16.count < textView.text.utf16.count)
}
func textViewDidChange(_ textView: UITextView) {
var indexPosition : Int?
updateCharacterCount()
self.mainScrollView.isScrollEnabled = false
if textView.text != "" {
inputIndex = (String(textView.text.utf16) as NSString?)?.substring(with: NSRange(location: 0, length: inputRange!.location)).count
cursorPosition = inputIndex! + 1
inputText = textView.text!
textViewEndIndex = textView.text.unicodeScalars.endIndex.utf16Offset(in: textView.text)
if #available(iOS 10.2, *) {
inputText = textView.text.replaceEmoji(with: "#")
} else {
//Fallback on earlier versions
}
if inputChar == "" || inputChar == " "{
indexPosition = cursorPosition!-2
}
else {
indexPosition = cursorPosition!-1
}
guard let enteredText = inputText?.utf16.subString(from: 0, to: indexPosition!) else { return }
guard let lastdelimiterposition = enteredText.lastIndexPosition(of: "#") else { return }
hashwordstartIndex = lastdelimiterposition
checkhashinword = inputText?.utf16.subString(from: lastdelimiterposition, to: indexPosition!)
if inputChar == "" || inputChar == " "{
spaceCharactersCheck = 1
}
if spaceCharactersCheck == 1{
checkhashword = checkhashinword?.components(separatedBy: " ").filter({!$0.contains("#")}).joined(separator: " ")
checkhashedword = checkhashinword?.components(separatedBy: " ").filter({$0.contains("#")}).joined(separator: " ")
}
if let checkhashinword = checkhashinword {
if checkhashinword.utf16.count > 1 && !(checkhashinword.contains(" ")){
self.presenter.returnHashTagsData((checkhashinword.utf16.subString(from: 1, to: checkhashinword.utf16.count-1))!)
}
}
}
else {
self.toggleTableView(toggle: true)
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let rowSelected = tableView.cellForRow(at: indexPath)?.textLabel?.text
var textViewValue : String?
textViewValue = textView_Desc.text!
let startIndex = String.Index(utf16Offset: hashwordstartIndex!, in: String(textViewValue!))
let hashwordendIndex = String.Index(utf16Offset: inputIndex!, in: String(textViewValue!))
let range = startIndex...hashwordendIndex
if var strNewText = textViewValue?.components(separatedBy: "#") {
if strNewText.count > 1 {
if let textlabel = rowSelected {
strNewText[strNewText.count - 1] = textlabel
}
}
if var rowSelected = rowSelected {
if let checkhashword = checkhashword
{
if checkhashword != ""
{
rowSelected = rowSelected.appending(" ").appending(checkhashword).appending(String(checkhashedword!))
}
}
if textViewValue != ""{
if textViewEndIndex != inputIndex {
do {
if let result = textViewValue?.replaceSubrange(range, with: rowSelected)
{
print("result:\(result)")
}
else {
throw RangeException.notaValidRange
}
}
catch {
}
}
}
spaceCharactersCheck = 0
checkhashword = ""
checkhashedword = ""
}
let combinedText = strNewText.joined(separator: "#")//.appending(" ")
if combinedText.count-1 > MediaPostViewController.descCharacterLimits {
if textView_Desc.text.count-1 > MediaPostViewController.descCharacterLimits {
toggleTableView(toggle: true)
return
}
}
if textViewEndIndex != inputIndex {
textView_Desc.text = textViewValue
}
else {
textView_Desc.text = combinedText.replacingOccurrences(of: "##", with: "#")
}
lbbbl_DescCount.text = "\(textView_Desc.text.count)/\(MediaPostViewController.descCharacterLimits)"
}
toggleTableView(toggle: true)
self.hideScrollView.isHidden = false
self.view.bringSubviewToFront(hideScrollView)
}
}
Use case :
Trying to implement hashtags similar to Instagram.
Approach:
There is a textView and I’ve added a tableView beneath that. The tableView gets data from API call based on the user input in textView. For instance, if user types #a then I show the tableView and tableView is loaded with suggestions like (#abc,#abcd, etc) from API call. The user can select a row and after selection, I hide the tableView. It works perfectly fine when the user enters hashtags between a text like #abc #insta and say if the user tries to type #ba between this #abc #insta it gets perfectly inserted after user chooses a suggestion from tableView (like #abc#bat#insta)
Problem:
When I have emojis the text replaces the emoji. For eg: if the user enters emoi#a then it will get a list starting with #a (like #abc,#ab etc) and now if the user selects #abc then it populates the textView like #abc#a and emoji disappears.
I don’t see a solution to this problem in any Github repositories.
Has anyone faced a similar problem?

Emoji skin-tone detect

Following this post I tried to update the code from Swift 2.0 to Swift 5.0 to check which emojis have skin tones available or not and other variations already present.
My updated code in detail:
extension String {
var emojiSkinToneModifiers: [String] {
return [ "🏻", "🏼", "🏽", "🏾", "🏿" ]
}
var emojiVisibleLength: Int {
var count = 0
enumerateSubstrings(in: startIndex..<endIndex, options: .byComposedCharacterSequences) { (_, _, _, _) in
count = count + 1
}
return count
}
var emojiUnmodified: String {
if self.count == 0 {
return ""
}
let range = String(self[..<self.index(self.startIndex, offsetBy: 1)])
return range
}
var canHaveSkinToneModifier: Bool {
if self.count == 0 {
return false
}
let modified = self.emojiUnmodified + self.emojiSkinToneModifiers[0]
return modified.emojiVisibleLength == 1
}
}
And use this with an array:
let emojis = [ "πŸ‘", "πŸ‘πŸΏ", "🐸" ]
for emoji in emojis {
if emoji.canHaveSkinToneModifier {
let unmodified = emoji.emojiUnmodified
print(emoji)
for modifier in emoji.emojiSkinToneModifiers {
print(unmodified + modifier)
}
} else {
print(emoji)
}
}
The output:
πŸ‘πŸ‘πŸ»πŸ‘πŸΌπŸ‘πŸ½πŸ‘πŸΎπŸ‘πŸΏ πŸ‘πŸΏπŸ‘πŸΏπŸ»πŸ‘πŸΏπŸΌπŸ‘πŸΏπŸ½πŸ‘πŸΏπŸΎπŸ‘πŸΏπŸΏ 🐸🐸🏻🐸🏼🐸🏽🐸🏾🐸🏿
assigns variations to emojis that do not have them or that already is instead of: πŸ‘πŸ‘πŸ»πŸ‘πŸΌπŸ‘πŸ½πŸ‘πŸΎπŸ‘πŸΏ πŸ‘πŸΏ 🐸
I suppose enumerateSubstringsInRange is incorrect and self.characters.count now became self.count easy and correct to count one emoji (composed) compared to before Swift 4 but maybe not useful in this case. What am I not seeing wrong?
Thanks
A "hack" would be to compare the visual representation of a correct emoji (like "🐸") and a wanna-be emoji (like "🐸🏽").
I've modified your code here and there to make it work:
extension String {
static let emojiSkinToneModifiers: [String] = ["🏻", "🏼", "🏽", "🏾", "🏿"]
var emojiVisibleLength: Int {
var count = 0
let nsstr = self as NSString
let range = NSRange(location: 0, length: nsstr.length)
nsstr.enumerateSubstrings(in: range,
options: .byComposedCharacterSequences)
{ (_, _, _, _) in
count = count + 1
}
return count
}
var emojiUnmodified: String {
if isEmpty {
return self
}
let string = String(self.unicodeScalars.first!)
return string
}
private static let emojiReferenceSize: CGSize = {
let size = CGSize(width : CGFloat.greatestFiniteMagnitude,
height: CGFloat.greatestFiniteMagnitude)
let rect = ("πŸ‘" as NSString).boundingRect(with: size,
options: .usesLineFragmentOrigin,
context: nil)
return rect.size
}()
var canHaveSkinToneModifier: Bool {
if isEmpty {
return false
}
let modified = self.emojiUnmodified + String.emojiSkinToneModifiers[0]
let size = (modified as NSString)
.boundingRect(with: CGSize(width : CGFloat.greatestFiniteMagnitude,
height: .greatestFiniteMagnitude),
options: .usesLineFragmentOrigin,
context: nil).size
return size == String.emojiReferenceSize
}
}
Let's try it out:
let emojis = [ "πŸ‘", "πŸ‘πŸΏ", "🐸" ]
for emoji in emojis {
if emoji.canHaveSkinToneModifier {
let unmodified = emoji.emojiUnmodified
print(unmodified)
for modifier in String.emojiSkinToneModifiers {
print(unmodified + modifier)
}
} else {
print(emoji)
}
print("\n")
}
And voila!
πŸ‘
πŸ‘πŸ»
πŸ‘πŸΌ
πŸ‘πŸ½
πŸ‘πŸΎ
πŸ‘πŸΏ
πŸ‘
πŸ‘πŸ»
πŸ‘πŸΌ
πŸ‘πŸ½
πŸ‘πŸΎ
πŸ‘πŸΏ
🐸

How to fix memory issues given by Instrument tools in Swift?

I have memory issues, especially for the error
XPC connection interrupted
The screen is freezing for a few seconds..
So, I've been learning how to use the Instruments tools and try to fix this error. However, I've been trying to find the error in my code and it's apparently not the fault of my code but maybe the libraries?
As a result of this test, I've got some warnings (the purple ones):
Memory Issues (3 leaked types):
- 1 instance of _DateStorage leaked (0x10b1eb060)
- 1 instance of OS_dispatch_data leaked (0x10b0b1ac0)
- 1 32-byte malloc block leaked (x10b1eb040)
Could you tell me how to fix these warnings, knowing there is no backtrace available? Or how could I find somewhere that could tell me to fix those?
EDIT:
Thanks to Instrument tools, I found the function that caused the problem! So, I don't know if it is really about memory or Idk but here's the function!
The accurate and useful error I get is : "Closure #1 in closure #1 in MessagesTableViewController.getLastMessages"
I found here What is a closure #1 error in swift?, the error is probably caused by forced optional types. So, I am going to try to remove those.
func getLastMessages(cell: ContactMessageTableViewCell, index: IndexPath) {
// first, we get the total number of messages in chatRoom
var numberOfMessagesInChatRoom = 0
let previousCellArray = self.tableView.visibleCells as! [ContactMessageTableViewCell]
var index1 = 0
var messages = [JSQMessage]()
var sortedMessages = [JSQMessage]()
var messagesSortedByChatRooms = [String: [JSQMessage]]()
var doesHaveMessagesCount = false
var doesHaveSortedMessagesCount = false
let firstQuery = Constants.refs.databaseChats.queryOrderedByKey()
_ = firstQuery.observe(.childAdded, with: { [weak self] snapshot in
if let data = snapshot.value as? [String: String],
let id = data["sender_id"],
let name = data["name"],
let text = data["text"],
let chatRoom = data["chatRoom"],
!text.isEmpty
{
if let message = JSQMessage(senderId: id, displayName: name, text: text)
{
messages.append(message)
var arrayVariable = [JSQMessage]()
// we wanna get all messages and put it in the array corresponding to the chat room key
if messagesSortedByChatRooms[chatRoom] != nil { // if there is already the chatRoom key in dictionary
if let message1 = messagesSortedByChatRooms[chatRoom] {
arrayVariable = message1
}
arrayVariable.append(message)
messagesSortedByChatRooms[chatRoom] = arrayVariable
} else { // if there isn't the chatRoom key
arrayVariable.append(message)
messagesSortedByChatRooms[chatRoom] = arrayVariable
}
}
}
DispatchQueue.main.async {
// we have to sort messages by date
for (chatRoom, messagesArray) in messagesSortedByChatRooms {
var loopIndex = 0
var lastMessage: JSQMessage?
var array = [JSQMessage]()
for message in messagesArray { // we run through the messages array
array.removeAll()
loopIndex += 1
if loopIndex != 1 {
if message.date > lastMessage!.date {
array.append(message)
messagesSortedByChatRooms[chatRoom] = array
} else {
array.append(lastMessage!)
messagesSortedByChatRooms[chatRoom] = array
}
} else {
lastMessage = message
if messagesArray.count == 1 {
array.append(message)
messagesSortedByChatRooms[chatRoom] = array
}
}
}
}
if !doesHaveMessagesCount {
//doesHaveMessagesCount = true
// we have the number of chats in database
let secondQuery = Constants.refs.databaseChats.queryOrderedByPriority()
_ = secondQuery.observe(.childAdded, with: { [ weak self] snapshot in
if let data = snapshot.value as? [String: String],
let id = data["sender_id"],
let name = data["name"],
let text = data["text"],
let chatRoom = data["chatRoom"],
!text.isEmpty
{
if let message = JSQMessage(senderId: id, displayName: name, text: text)
{
index1 += 1
if chatRoom != nil {
if let unwrappedSelf = self {
if unwrappedSelf.sortedChatRoomsArray.contains(chatRoom) {
sortedMessages.append(message)
for (chatRoomKey, messageArray) in messagesSortedByChatRooms {
unwrappedSelf.lastMessages[chatRoomKey] = messageArray[0]
}
}
}
}
if let unwrappedSelf = self {
if index1 == messages.count && chatRoom != unwrappedSelf.roomName {
sortedMessages.append(JSQMessage(senderId: id, displayName: name, text: "no message"))
}
}
}
}
DispatchQueue.main.async {
if let unwrappedSelf = self {
if !doesHaveSortedMessagesCount {
//doesHaveSortedMessagesCount = true
if unwrappedSelf.sortedChatRoomsArray.indices.contains(index.row) {
if unwrappedSelf.lastMessages[unwrappedSelf.sortedChatRoomsArray[index.row]] != nil {
if unwrappedSelf.lastMessagesArray.count != 0 {
let currentChatRoom = unwrappedSelf.sortedChatRoomsArray[index.row]
if unwrappedSelf.lastMessages[unwrappedSelf.sortedChatRoomsArray[index.row]]?.text != "no message" {
if UUID(uuidString: unwrappedSelf.lastMessages[unwrappedSelf.sortedChatRoomsArray[index.row]]!.text) == nil {
cell.contactLastMessageLabel.text = unwrappedSelf.lastMessages[unwrappedSelf.sortedChatRoomsArray[index.row]]?.text
} else {
cell.contactLastMessageLabel.text = "New image"
}
} else {
cell.contactLastMessageLabel.text = ""
cell.contactLastMessageLabel.font = UIFont(name:"HelveticaNeue-Light", size: 16.0)
}
if unwrappedSelf.lastMessagesArray.indices.contains(index.row) {
if unwrappedSelf.lastMessagesArray[index.row] != unwrappedSelf.lastMessages[unwrappedSelf.sortedChatRoomsArray[index.row]]?.text {
if unwrappedSelf.lastMessages[unwrappedSelf.sortedChatRoomsArray[index.row]]?.senderId != PFUser.current()?.objectId {
if unwrappedSelf.lastMessages[unwrappedSelf.sortedChatRoomsArray[index.row]]?.text != "no message" {
cell.contactLastMessageLabel.font = UIFont(name:"HelveticaNeue-Bold", size: 16.0)
}
var numberOfDuplicates = 0
for cell in previousCellArray {
if cell.contactLastMessageLabel.text == unwrappedSelf.lastMessages[unwrappedSelf.sortedChatRoomsArray[index.row]]?.text {
numberOfDuplicates += 1
}
}
if numberOfDuplicates == 0 {
if unwrappedSelf.selectedUserObjectId != "" {
unwrappedSelf.changeCellOrder(index: index.row, selectedUserObjectId: unwrappedSelf.selectedUserObjectId, lastMessage: unwrappedSelf.lastMessages[unwrappedSelf.sortedChatRoomsArray[index.row]]!.text)
} else {
unwrappedSelf.changeCellOrder(index: index.row, selectedUserObjectId: "none", lastMessage: unwrappedSelf.lastMessages[unwrappedSelf.sortedChatRoomsArray[index.row]]!.text)
}
} else {
unwrappedSelf.tableView.reloadData()
}
cell.activityIndicatorView.stopAnimating()
}
} else {
cell.contactLastMessageLabel.font = UIFont(name:"HelveticaNeue-Light", size: 16.0)
}
}
}
} else {
}
}
}
}
}
})
}
}
})
}
FINL EDIT: I put a closure inside of another closure so it created an infine loop ;)

What is the replacement for AKSampler.loadMelodicSoundFont in AudioKit 4.2?

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

NSExpression Calculator in Swift

I am trying to duplicate Need to write calculator in Objective-C in Swift but my code is not working.
import Foundation
var equation:NSString = "5*(2.56-1.79)-4.1"
var result = NSExpression(format: equation, argumentArray: nil)
println(result)
As already said in a comment, you have to call expressionValueWithObject()
on the expression:
let expr = NSExpression(format: equation)
if let result = expr.expressionValueWithObject(nil, context: nil) as? NSNumber {
let x = result.doubleValue
println(x)
} else {
println("failed")
}
Update for Swift 3:
let expr = NSExpression(format: equation)
if let result = expr.expressionValue(with: nil, context: nil) as? Double {
print(result) // -0.25
} else {
print("failed")
}
Details
Xcode 9.4.1, Swift 4.1
Xcode 10.2.1 (10E1001), Swift 5
Solution
import Foundation
extension String {
private func allNumsToDouble() -> String {
let symbolsCharSet = ".,"
let fullCharSet = "0123456789" + symbolsCharSet
var i = 0
var result = ""
var chars = Array(self)
while i < chars.count {
if fullCharSet.contains(chars[i]) {
var numString = String(chars[i])
i += 1
loop: while i < chars.count {
if fullCharSet.contains(chars[i]) {
numString += String(chars[i])
i += 1
} else {
break loop
}
}
if let num = Double(numString) {
result += "\(num)"
} else {
result += numString
}
} else {
result += String(chars[i])
i += 1
}
}
return result
}
func calculate() -> Double? {
let transformedString = allNumsToDouble()
let expr = NSExpression(format: transformedString)
return expr.expressionValue(with: nil, context: nil) as? Double
}
}
Usage
"3*(3-1)-5".calculate()
Full sample
func test(_ expressrion: String) {
if let num = expressrion.calculate() {
print("\(expressrion) = \(num)")
} else {
print("\(expressrion) = nil")
}
}
test("3*(3-1)-5")
test("5.2*(2-1.79)-5.1")
test("11/5")
Results

Resources