Today we are thrilled to introduce to you our newest product - CloudMounter. This system utility allows mounting cloud storages and web servers as additional drives on your Mac.
The way it works is really simple: choose the service to mount with the help of a handy connections manager UI, login using your credentials and the mounted service or server will appear as an additional drive in Finder.
Supported services:
Google Drive
Microsoft OneDrive
Dropbox
Amazon S3
FTP\FTPS\SFTP
WebDAV
As for the security, CloudMounter does not save, store or use in any way your private data. All passwords are securely kept in Mac OS Keychain and sent directly to the servers through SSL-encrypted channels.
Try CloudMounter and see what advantages a centralized cloud manager can provide.
Anya is live and ready to show you everything. Watch her strip, dance, and perform exclusive shows just for you. Interact in real-time and make your fantasies come true.
✓ Live Streaming✓ Interactive Chat✓ Private Shows✓ HD Quality
Anya is LIVE right now
FREE
Free to watch • No registration required • HD streaming
We, at Eltima Software, are happy to announce the release of our newest app - Uplet is a bulk Instagram uploader for Mac. This awesome app allows you to upload and post any number of photos to your Instagram account in a single click.
The interface of Uplet is really simple. All you need to do is login to your Instagram account, drag-and-drop photos you want to upload and post all of them in one go, directly from your Mac.
If required, you can edit pictures one-by-one. Uplet allows you to:
post photos in their original dimensions or crop them square;
upload multiple photos at a time;
keep the original quality of images;
add captions with hashtags and emoticons.
Please note that Uplet is not affiliated to Instagram, so you will need your Instagram account to use the app. Uplet is available for download at Mac App Store. More information can be found on the product’s web-site and our pages on social networks - Facebook, Twitter and YouTube channel.
With the release of Commander One, our two-pane file manager for OS X, we have realized that some of our users run Apple devices on the client side and Windows on the server side. In order to help those particular users we have tested some SFTP servers for Windows to verify they work well with our software.
Among the various options available on the market, Syncplify.me Server! stands out both in terms of security and ease of use. You can literally set up a very secure, PCI and HIPAA compliant, SFTP server in 5 minutes, with automatic configurations for the most common use-case scenarios such as maximum security, legacy client support, or highly optimized backup target. And the more advanced users will enjoy its support for Active Directory, scripting, event-handling, and above all its automatic self-protecting and self-healing capabilities.
Enjoy the power and flexibility of Syncplify.me Server! coupled with Commander One, and deploy your own robust and reliable turn-key secure file transfer system.
Eltima Software team is glad to announce the start of our brand new project - Swordbox - the most convenient way to keep all your passwords safe, accessible, and organized. The only thing required is a Dropbox account.
You will get access to all your passwords from any computer or mobile device at any time. Any change you make on one device is immediately available on your other devices. All information is kept safe and you are the only one who knows the master key.
You can be sure that all your passwords are as safe as can be because:
database is carefully encrypted
only you know master key
we can't access your passwords
two-step verification is performed
database locks based on user inactivity
authorization takes place at Dropbox secure server
If for some reason you do not want to use Dropbox, we are working on other services support, like Google Drive or Eltima’s own sync service. You can also suggest a service you prefer to use.
Swordbox will be available for Windows, Android, Mac, and iOS.
Visit Eltima’s official website for more information.
The latest Commander One, our dual pane file manager, is released
The new version brings in various enhancements that will make it work better with OS X 10.11 El Capitan. The app has migrated to Swift 2.0 to become even more stable and work faster. Commander One now supports Amazon S3 storage service and is capable of integrating as many Google Drive accounts as required. There is no need to copy their data on Mac to access and manage it.
Below is the entire list of what you get with the latest Commander One.
Version 1.2
There are various enhancements to help the app work better with OS X 10.11 El Capitan. Migration to Swift 2.0 has improved Commander One in terms of stability and speed.
Additions:
Access and manage data stored on Amazon S3 (available in PRO Pack)
Access and manage contents of Google Drive (PRO Pack)
Mount Dropbox as a disk, Share it or use other Dropbox options (PRO Pack)
New languages support – Spanish, Czech, Chinese
Improvements:
Queuing file operations, including those that have already started.
Processing file operations in the background. This allows working with multiple Commander One windows.
Displaying hidden files as translucent. This helps identify them better.
Displaying time remaining for modal operations such as copy, move, etc.
Fixes:
Connection interrupted when downloading large files
You can download new version from official website
Anya is live and ready to show you everything. Watch her strip, dance, and perform exclusive shows just for you. Interact in real-time and make your fantasies come true.
✓ Live Streaming✓ Interactive Chat✓ Private Shows✓ HD Quality
Anya is LIVE right now
FREE
Free to watch • No registration required • HD streaming
Commander One 1.1 is fully compatible with OS X 10.11 El Capitan
We have all been waiting eagerly for OS X 10.11 El Capitan to arrive - and it is here at last! Apart from UI and usability improvements the major update of the release is related to security system, we will discuss it below.
To enhance OS X security and reliability Apple introduces new safety system officially called System Integrity Protection (SIP), it is also known as "Rootless" feature.
The system accomplishes two tasks:
restricts root access
protects system integrity on the disk and memory
Thus your system’s update to El Captain will disable applications that inject code into OS X processes. This will also affect file managers that are not fully compatible with the new system. Such apps change the processes by adding features whilst they are running. OS X 10.11 El Capitan however does not allow making such changes.
New security system protects system files. The latter can be modified only by Apple installers, and also via Software update. Apple decided that certain actions cannot be performed by third party software. This means that the app that “inject” themselves into system processes will not work anymore.
I am happy to announce that updating your system to OS X 10.11 El Capitan will have no impact on Commander One 1.1. It is absolutely independent program that does not conflict with the new Apple security system, and you can keep utilizing benefits offered by the app.
Download Commander One 1.1
P.S.//If after upgrading to OS X 10.11 El Capitan your file manager does not function anymore, we will give you 50% discount on Commander One PRO Pack - provided you have a proof that you have purchased other file manager before.
We are delighted to announce the release of Commander 1.1, the newest version of our dual pane file manager. We fixed some bugs and added some features according to our users’ suggestions.
Commander One 1.1 brings in:
Multiple enhancements to provide better compatibility with OS X 10.11 El Capitan
Additions:
Tar.Z, tar.lzma and tar.xz support (PRO Pack)
FTPS protocol support (PRO Pack)
hanging file permissions via FTP/ FTPS/SFTP protocols (PRO Pack)
Selecting a program to open a file, ‘Open with’ command is accessible via File and context menus
German and Dutch languages support
Improvements:
Detecting computers that use NetBIOS protocol and displaying them in the list of network computers
Truncating long file names with an ellipsis in the middle – works in Thumbs and Brief view modes. To see a full name, hover over it.
Fixes:
Erroneous search through the files contents using regular expressions
Wrong creation time of a file displayed when copying it from iOS, MTP or FTP to Mac
The new version is already available for download from official website.
New file manager for Mac created in Swift – Commander One
We are delighted to introduce to you our new product - meet Commander One. This file manager features a dual-pane interface and a number of powerful features. Commander One is designed to make browsing and managing your Mac’s content smoother and faster thus increasing efficiency of your workflow.
As we mentioned earlier, this is the first product we have developed in Swift, and we are pleased with the result. The dual-pane interface for easy moving and copying files from one place to another; multi-tab browsing for eliminating clutter; advanced search for locating files quickly; file operations queueing, hotkeys setup, ZIP support and much more - Commander One will surely become your indispensable assistant.
PRO Pack of the application offers additional benefits: built-in fast FTP client that enables you to connect to remote server through FTP or SFTP; easy access to data on your devices, both iOS and Android; compression and extraction of ZIP, RAR, TBZ, TGZ, 7z files; integration of any number of Dropbox accounts, and more.
This is what you get with Commander One:
2 panels with 3 view modes and unlimited tabs
Multiple selection
File operations queue
Rename files during copy and move operations
Set up custom hotkeys for any action
Work with local and network drives
Customizable fonts & colors
Spotlight search
Show hidden files
Search in ZIP archives
Advanced search with regular expressions
ZIP archives support with compressing, extracting and full access
Bonjour computers
Root Access
Preview multiple types of files incl. binary and hex
Extra features of PRO Pack:
RAR support with extracting, full access and search
TarGz with compressing, extracting, search, and full access
7zip with compressing, extracting, search, and full access
Mount iOS devices
Mount MTP devices
Process viewer
Mount Dropbox account as a drive
Terminal Emulator
FTP Manager
Share Dropbox Links
Themes
Avail yourself of our special offer - PRO Pack is now available for free trial for 15 days. Make most of it!
It’s been a while since we posted last, but all for a good reason – all this time we’ve been working hard on our new product we’ve already told you about. The product is to be launched, and we would like to share some details with you.
You might remember that this is our first project on Swift, that is why we found it to be sort of challenging. The happier we are now to announce we accomplished this ambitious task – a lot of new experience and working on it was fun.
What is Commander One all about? It is basically a file manager for Mac, equipped with a double pane interface.
This is how a list of functions – a preliminary one – looks:
Double pane to list files
3 display modes (Full, Brief, Thumbs)
Tabs support
Hidden files display
File operations queue
Built-in search with regular expression support
App’s own file viewer
Root Access mode
and much more
More information will be available on the release day. Apart from what is mentioned above Commander One supports archiving (zip, tar, rar, 7zip). It can also mount iOS, MTP or Dropbox accounts as drives; a full-fledged FTP Manager combined with built-in Terminal turn Commander One into an indispensable tool for developers.
Are you as excited as we are? Would like to be the first to check out our new product? Follow the link below and subscribe to get the release announcement.
Remember me telling about Eltima's new thrilling project in the beginning of the summer? Good news! Our team is getting closer to alpha, and what we are doing right now is adding secondary functions and bugfixing.
ABOUT SWIFT
It was decided to write the new project in Swift. Swift certainly has its pros and cons as any programming language.
I've defined the following cons:
Each xcode beta version brought internal language changes, so we had to fix the code to match the new version.
We had to use sometimes Objective-C, as Swift did not let us do all the things we wanted.
Performance of an app written in Swift is still lower than of an app written in Objective-C, so that's why we had to use Objective-C code sometimes.
Pros:
Swift is a new language featuring many handy tools for speeding up the development, like enums, pattern mathing, generics, tuples, property observers and others.
With Swift you can finally get rid of certain things which were used barely for compatibility with C/C++ and Objective-C (define, attribute syntax).
Since a programming language is merely a tool in a programmer's hands, the handier the tool is, the more productive a developer can be. By using Swift in a real project, we gained a useful and interesting experience. That we can say for sure.
NEW AWESOME PRODUCT
A few words about our new app. That is an impressive and ambitious project. But we won't tell you any more details at this stage, as alpha and beta tests are still ahead. We hope to release public beta version be the end of the year. Cannot promise you anything specific, as we have tons of work to do yet.
We'll tell more details about the project, as well as our challenges, once the alpha testing is completed.
ICON FOR THE NEW APP
What we'd like to show you today is an icon for the new app.
Guess what app it is intended for.
Leave your variants in the comments. The first who guesses right will get the free license. And everybody who participated in the quiz will get access to the public beta.
Anya is live and ready to show you everything. Watch her strip, dance, and perform exclusive shows just for you. Interact in real-time and make your fantasies come true.
✓ Live Streaming✓ Interactive Chat✓ Private Shows✓ HD Quality
Anya is LIVE right now
FREE
Free to watch • No registration required • HD streaming
In our previous post we used UART signals to connect to Sim900. We also tried to make a call and send a text message.
So far, we didn't consider too much responses to commands which are sent to the module. Today we're going to explore them in detail.
As an example, we'll study getting the module's status. Also, we'll try to get incoming calls and text messages.
Let's start from getting messages from the module. Earlier we used sleep function, as we didn't know for sure when exactly the data is ready to be read. Let's use pool function, so that the thread wakes up once the data is available for reading.
We don't have to wait long as we get a response once the command is processed. By default, the module sends echo of the received command. So this should be taken into account when reading the response.
Echo is followed by newline and status of the command execution - ОК or ERROR. You can disable getting echo of your command by “ATE0” command. The response will be of “+:“ format.
Here are some examples:
Getting info about operator:“AT+COPS?”
We get the response:
+COPS: 0,0,”AT&T”
The response is 0,0,”AT&T”
The first parameter indicates that GSM network was chosen automatically. The third parameter stays for the operator name.
Here's how this command can be sent in Swift:
let cmd = "AT+COPS?\r\n" write(sim900descriptor, cmd, UInt(countElements(cmd)))
Sending all the subsequent commands in this article is similar to this one.
You can see what other commands return:
AT+CSPN? - SIM card's operator
AT+CSQ - signal strength
AT+CBC - voltage, battery level
AT+COPS=? - available operators in the coverage area
In our previous post we learned how to make an outgoing call. When receiving an incoming call, we get “RING” string at certain intervals. We can response to the call by “ATA” command or reject the call by “ATH” command.
In order not to get incoming calls at all, reject them by AT+GSMBUSY=1 command.
AT+CLCC command is needed to get the call's status. Here are the command parameters:
+CLCC: ,,,,
To get the telephone number of the caller, execute AT+CLIP=1. The answering machine will send
RING together with the telephone number:
+CLIP:,...
If you get +CPIN: SIM PIN, it means that working with a SIM card is possible after entering the PIN code via AT+CPIN= command.
OK, so now we know how to get and reject incoming calls and get the status of the call.
In our previous post we learned how to send a text message. Incoming messages are received in +CMTI: "SM", format.
stays for the number assigned to the saved message
We'll use this number to get the contents of the message via AT+CMGR command:
AT+CMGR=,
Now we get the response to CMGR command:
+CMGR: "REC UNREAD","+496967733496","","14/10/24,16:14:17+12"
Message from phone
Clean out your text messages periodically to free space, otherwise you won't be able to send or get a new message. To delete text messages from all folders, execute at+cmgd=1,4
To view all messages, execute AT+CMGL.
So, these are the basic functions of GSM phone based on Sim900 radio module. Here's a code sample in Swift we've prepared for you. Sim900.swift contains more methods to use in your work.
Hi there. Today we are going to get a closer look at Sim900 radio module which we mentioned in our last post.
So here we are with a board with Sim900 radio module, where the main contacts have already been withdrawn into the contact area for SIM card. Also, serial interface connections, as well as outlets for microphone and linear output, are available.
To make sure that the drivers are installed, type in the Terminal:
ls /dev/tty.*
Next we have to find our device. Here's what our device looks like:
/dev/tty.PL2303-00001014
Connect contacts. Rx/Tx voltage of UART signals is 3.3V, which permits to connect contacts to the board directly. 5V power supply is provided by UART as well. Thus, we are using all the UART contacts: Rx/Tx, +5V and GND.
Sim900 module by default requires 115200 bit/s connection speed, 8 bits per symbol and 1 stop bit. All other options should be disabled.
UART is rather simple serial interface, even simpler than Serial Com port, but we can use the same components for work with it, limited only by the data exchange on the specified transfer speed.
OS X lets us work with serial port via tеrmios, which is the part of POSIX. All we have to do is tune port settings and try to transfer data.
For data exchange we'll use Swift programming language. Here's how setting port options looks like:
func openWithDevicePath(devicePath:String, error:NSErrorPointer?) -> Bool { let d : Int32 = open(devicePath, O_RDWR | O_NOCTTY | O_NDELAY) if d == -1 { if let err = error { err.memory = NSError(domain: "could not open device", code: -1, userInfo: nil) } return false } var tty = UnsafeMutablePointer.alloc(1) if tcgetattr(d, tty) != 0 { close(d) if let err = error { err.memory = NSError(domain: "tcgetattr failed", code: -1, userInfo: nil) } return false } if cfsetospeed (tty, 115200) == -1 { close(d) if let err = error { err.memory = NSError(domain: "cfsetospeed failed", code: -1, userInfo: nil) } return false } if cfsetispeed (tty, 115200) == -1 { close(d) if let err = error { err.memory = NSError(domain: "cfsetispeed failed", code: -1, userInfo: nil) } return false } tty.memory.c_cflag = (tty.memory.c_cflag & ~UInt(CSIZE)) | UInt(CS8); // 8-bit chars tty.memory.c_iflag = ~UInt(IGNBRK) // ignore break processing tty.memory.c_lflag = 0 // no signaling chars, no echo, tty.memory.c_oflag = 0 // no remapping, no delays tty.memory.c_iflag &= ~(UInt(IXON) | UInt(IXOFF) | UInt(IXANY)) // shut off xon/xoff ctrl tty.memory.c_cflag |= (UInt(CLOCAL) | UInt(CREAD)) // ignore modem controls, tty.memory.c_cflag &= ~(UInt(PARENB) | UInt(PARODD)) // shut off parity tty.memory.c_cflag |= 0 tty.memory.c_cflag &= ~UInt(CSTOPB) // One stop bit tty.memory.c_cflag &= ~((UInt(CCTS_OFLOW) | UInt(CRTS_IFLOW))) // Disable RTS/CTS if tcsetattr (d, TCSANOW, tty) != 0 { close(d) if let err = error { err.memory = NSError(domain: "cfsetispeed failed", code: -1, userInfo: nil) } return false } sim900descriptor = d return true }
The function should be always called before starting work with Sim900. The data is sent and received via read and write functions, that's why the command execution will be realized via timer. Receiving data from the device is similar to the work of Terminal. In the next posts we'll give a closer examination of Pooling mode, where the data is received.
So let's execute a simple command to test connection to the module - “AT”.
main function in Swift looks like:
var error : NSError? let sim900descriptor = sim900DescriptorWithDevicePath("/dev/tty.PL2303-00001014", &error) if sim900descriptor == -1 { if let e = error { println("Error: \(e.localizedDescription)") } exit(1) } let cmd = "AT\r\n" write(sim900descriptor, cmd, UInt(countElements(cmd))) sleep(2) var buffer = UnsafeMutablePointer.alloc(Int(128)) memset(buffer, 0, 128) let bitesReaded = read(sim900descriptor, buffer, 127) if let response = String.fromCString(buffer) { println("Response: \(response)") }
Data is transferred via write call, as you've probably noticed.
If OK is returned, data transfer settings are correct.
Here's a sample which allows sending an SMS message and making an outgoing call. Let's describe two simple commands.
We are going to dial a telephone number, so we need to pass a command in “ATD;” format, which has the following look:
Making a call is that easy. Sending an SMS message is a little harder and may remind you sending POST request to the Internet. Once the command is received, the module will wait for the body of the letter and 1A symbol.
To do this, we'll use command of “AT+CMGS=” format. Once it is sent, send the body of the message with 0x1A symbol in the end, in case the module didn't return an error.
Eltima team is starting the series of articles about using embedded devices with computers running OS X. Quite often we need to connect to devices remotely. Sometimes we need to send or get data, or to manage the device which is located far away and does not have Internet access.
To avoid inventing a new connection system, one can use already existing mobile coverage. We set our eyes on SIM900 - wireless GSM module, which is quite known on the market today.
The pros are primarily its affordable price (it costs something about USD 10.00) and simple usability. The radio module uses 5V input voltage and features low power consumption. It is stable in work and has operation temperature from -30°C to +85 °C. It is very compact in size (chip-size) and complies to CE, FCC, ROHS, PTCRB, GCF, AT&T and ICASA Certifications.
To get more info on SIM900, go to the manufacturer's website:
http://wm.sim.com/defaulten.aspx
The module uses UART (universal asynchronous receiver/transmitter) for data transmission, therefore it can be easily integrated into any platform via standard serial interface. POSIX library on OS X, as well as on other UNIX systems, lets us use serial ports.
Once serial connection is established, interaction is ensured via AT commands, which we'll study in detail in upcoming articles.
By sending AT commands to the module, one is able to:
make outgoing calls and get incoming calls;
get and send messages;
create TCP connection or send HTTP requests to Internet via GPRS;
send and get DTMF signals;
use sound input/output for work with calls.
Note that there is various supplemented firmware which can expand the functional with additional features, like work with MMS, SMTP/POP3, PING, geolocation, etc..
So how can Sim900 be implemented? There are several variants:
Alarm systems where events are sent via SMS messages or voice call. AT command should be sent in due time to make a call or send a text message to an end-user.
Remote device management. Managing DTMF signals of the incoming call are received and transferred to the computer attached to the module.
Radio module usage is not limited to PC connection. One can connect it via UART to Atmel tiny microcontrollers, which, by the way, can be connected to the module via serial interface as well. In this case, in rather small form factor (2,5 х 2,5 cm).
It's rather hard to find appropriate software to work with Sim900, since the functional should be specifically designed for integration into the definite infrastructure. Besides, advanced versions of Sim900 may differ. However, interaction via the serial interface can be carried out by terminal programs for modem management.
In our next articles, we'll highlight the peculiarities of Sim900 integration in order to implement connection to remote devices. Stay tuned!
This article provides easy step-by-step instructions allowing to create your first app in Swift. You'll create an application for searching the network for computers with enabled file sharing. With the help of this app, users will be able to search the network for computers which use AFP or SMB protocols for files and folders sharing, and connect to any of them.
Note: You'll need the latest version of Xcode 6.1.
To create a Swift project in Xcode:
Start Xcode.
Choose File > New > Project > OS X > Application > Cocoa Application.
Enter SharingBrowser into Product Name field and choose Swift from the Language drop-down list. Make sure Use Core Data checkbox is disabled, and click Next.
Select the folder to save the project to, and click Create.
A Swift project’s structure is nearly identical to an Objective-C project, with one important distinction: Swift has no header files. There is no explicit delineation between the implementation and the interface, so all the information about a particular class resides in a single .swift file.
Adding Classes to the Project
SharedPoint class
Create a new Swift class named SharedPoint. To do this, choose from the menu: File > New > File > OS X > Source > Swift. This class is designed to describe the compliance between file sharing service and its IP address.
SharedPoint.swift file contains a string with import statement:
import Foundation
This string is intended to import Objective-C Foundation system framework into the Swift project. Importing makes all API of the framework available in Swift.
Add the SharedPoint class definition to the file. Class definition starts with the key word class and is enclosed in curly brackets:
class SharedPoint { // class definition goes here }
Add three properties to the class:
netService – variable of NSNetService type – file sharing service on computer.
type – variable of String type – file sharing protocol (SMB or AFP).
ipAddress – variable of String type - computer's IP address in the network.
class SharedPoint { var netService:NSNetService var type:String var ipAddress:String }
If app is launched right away, the following compilation error is invoked:
Class ‘SharedPoint’ has no initializers.
This happens because Swift requires all class properties, the types of which are optional, to be initialized at class instantiation.
Add initializer with three named parameters to the class:
With this initializer you can create an instance of the SharedPoint class, passing named values to each of its parameters. Note that one cannot call the initializer without stating external parameters names as it will lead to error.
Add the Equatable protocol conformance to the SharedPoint class. It allows to define whether two values of one and the same type are identical. To do this, add the name of Equatable protocol to the SharedPoint class definition after the class name separated by a colon:
class SharedPoint:Equatable { // class definition goes here }
Create a new Swift class named SharedBrowser. The class is intended to search computers with enabled file sharing in the network. To implement the search option, we'll use NSNetServiceBrowser class, which defines an interface for finding published services on a network using multicast DNS. Due to the possible delays associated with network traffic, NSNetServiceBrowser objects perform browsing asynchronously by registering with the default run loop. Browsing results are returned to your app through delegate methods. To handle results from an NSNetServiceBrowser object, you must assign it a delegate.
Thus, to perform search, do the following:
Initialize an NSNetServiceBrowser instance and assign a delegate to the object.
Start the search.
Handle search results and other messages sent to delegate.
Each browser service performs one search operation at a time, so to perform multiple operations simultaneously, use several browser services. We'll need two browser services: the first one - to search the network for computers which use SMB protocol for files sharing, the second one - to search the network for computers which use AFP protocol.
From the NSNetServiceBrowserDelegate protocol declaration, we see that it requires a conforming type also to conform to the NSObjectProtocol protocol:
Since NSObject class conforms to the NSObjectProtocol protocol:
class NSObject : NSObjectProtocol { // class definition goes here }
we'll make it superclass for the SharedBrowser class:
class SharedBrowser:NSObject { // class definition goes here }
Add the following properties to SharedBrowser class:
afpType - constant of String type - registration name of AFP service, which works over TCP protocol (see Bonjour Names for Existing Service Types);
smbType - constant of String type - registration name of SMB service;
smbBrowser - constant of NSNetServiceBrowser type - browser for computers in the network that provide access to files and folders over SMB protocol;
afpBrowser - variable of NSNetServiceBrowser type - browser for computers in the network that provide access to files and folders over AFP protocol;
serviceList - the list of detected services (array of objects of NSNetService type);
sharedPointsList - array of objects of SharedPoint type, each of which can be used for connecting to a certain computer in the network with enabled sharing via the IP address over the supported sharing protocol.
class SharedBrowser:NSObject { let afpType:String let smbType:String var smbBrowser:NSNetServiceBrowser var afpBrowser:NSNetServiceBrowser var serviceList:[NSNetService] var sharedPointsList:[SharedPoint] }
To add initializer init() to SharedBrowser class, it's necessary to override the designated NSObject initializer of its superclass via the key word override.
Initializer first assigns values to class properties, then it delegates up to init() initializer of the NSObject class. Then the fully initialized object assigns itself as delegate for objects afpBrowser and smbBrowser.
Please pay attention that calling the superclass’s initializer init() precedes the setting of delegates. Otherwise we'll get the compilation error:
'self' used before super.init call.
Delegate of the NSNetServiceBrowser object must conform to the NSNetServiceBrowserDelegate protocol. To avoid the following compilation error:
Type ‘SharedBrowser’ does not conform to protocol ’NSNetServiceBrowserDelegate’,
add the name of the NSNetServiceBrowserDelegate protocol to the SharedBrowser class header after the name of its superclass, separated by comma:
class SharedBrowser:NSObject, NSNetServiceBrowserDelegate { //properties and methods }
The NSNetServiceBrowserDelegate protocol defines the optional methods implemented by delegates of NSNetServiceBrowser objects.
Let's study some of them:
netServiceBrowser(_,didFindService:,moreComing:) tells the delegate the browser found a service.
netService parameter of NSNetService type is the detected service.
moreServicesComing parameter of Bool type shows whether the browser expects any other additional services.
Add implementation of this method to SharedBrowser class. When calling this method, the detected service is to be added to serviceList array, and when the service search is over, update() method, which description is provided below, is to be called:
When calling this method, the service which became unavailable is to be deleted from serviceList array, and its corresponding elements should be deleted from sharedPointsList array. When the service search is over, update() method should be called.
func netServiceBrowser(aNetServiceBrowser: NSNetServiceBrowser, didRemoveService aNetService: NSNetService, moreComing: Bool) { let iService = find(serviceList, aNetService) if iService != nil { serviceList.removeAtIndex(iService!) } let subArray = sharedPointsList.filter({$0.netService == aNetService}) for item in subArray { let iSharedPoint = find(sharedPointsList, item) if iSharedPoint != nil { sharedPointsList.removeAtIndex(iSharedPoint!) } } NSLog("Became unavailable: \(aNetService)") if !moreComing { update() } }
find method returns the first index where `value` appears in `domain` or `nil` if `value` is not found. Elements must conform to the Equatable protocol.
filter method returns an array containing the elements of the receiver for which a provided closure indicates a match. In our case it's an array of elements of [SharedPoint] type, in which netService variable refers to the service which became unavailable.
netServiceBrowser(_,didNotSearch:) method tells the delegate that the search failed.
When calling this method, the message containing the error code will be displayed in console. One can get the error code via the NSNetServiceErrorCode key in errorDict dictionary.
func netServiceBrowser(aNetServiceBrowser: NSNetServiceBrowser, didNotSearch errorDict: [NSObject : AnyObject]) { NSLog("Search was not successful. Error code: \(errorDict[NSNetServicesErrorCode]!)") }
In update() method, for each service of serviceList array, we'll set object of SharedBrowser class as delegate, and start the process of service address resolution.
func update() { for service in serviceList { service.delegate = self service.resolveWithTimeout(5) } }
NSNetService object delegate must conform to the NSNetServiceDelegate protocol. The name of the NSNetServiceDelegate protocol should be added to the SharedBrowser class header.
class SharedBrowser:NSObject, NSNetServiceBrowserDelegate, NSNetServiceDelegate { //properties and methods }
The NSNetServiceDelegate protocol defines the optional methods implemented by delegates of the NSNetService objects.
We'll add this protocol conformance to the SharedBrowser class.
netServiceDidResolveAddress(_) method informs the delegate that the address for a given service was resolved.
func netServiceDidResolveAddress(sender: NSNetService) { for addressBytes in sender.addresses! { var inetAddress : sockaddr_in! var inetAddress6 : sockaddr_in6! //NSData’s bytes returns a read-only pointer to the receiver’s contents. var inetAddressPointer = UnsafePointer(addressBytes.bytes) //Access the underlying raw memory inetAddress = inetAddressPointer.memory if inetAddress.sin_family == __uint8_t(AF_INET) { } else { if inetAddress.sin_family == __uint8_t(AF_INET6) { var inetAddressPointer6 = UnsafePointer(addressBytes.bytes) inetAddress6 = inetAddressPointer6.memory inetAddress = nil } else { inetAddress = nil } } var ipString : UnsafePointer? //static func alloc(num: Int) -> UnsafeMutablePointer var ipStringBuffer = UnsafeMutablePointer.alloc(Int(INET6_ADDRSTRLEN)) if inetAddress != nil { var addr = inetAddress.sin_addr ipString = inet_ntop(Int32(inetAddress.sin_family), &addr, ipStringBuffer, __uint32_t (INET6_ADDRSTRLEN)) } else { if inetAddress6 != nil { var addr = inetAddress6.sin6_addr ipString = inet_ntop(Int32(inetAddress6.sin6_family), &addr, ipStringBuffer, __uint32_t(INET6_ADDRSTRLEN)) } } if ipString != nil { // Returns `nil` if the `CString` is `NULL` or if it contains ill-formed // UTF-8 code unit sequences. var ip = String.fromCString(ipString) if ip != nil { NSLog("\(sender.name)(\(sender.type)) - \(ip!)") var ext : String switch sender.type { case afpType : ext = "afp" case smbType : ext = "smb" default: ext = sender.type } sharedPointsList.append(SharedPoint(netService:sender, type:ext, ipAddress:ip!)) NSNotificationCenter.defaultCenter().postNotificationName( "NewSharedDetected", object: self) } } ipStringBuffer.dealloc(Int(INET6_ADDRSTRLEN)) } }
addresses property of NSNetService class returns an array of objects of [AnyObject] type, each of which contains the sockaddr structure, which can be used to connect to socket:
var addresses: [AnyObject]? { get }
When working with Cocoa APIs, it is common to receive an array with a type of [AnyObject], or “an array of values of any object type”. This is because Objective-C does not have explicitly typed arrays. Though, in the most of cases you can be sure of the type of objects contained in such an array, basing on the information about the API provided by the array.
For example, in our case it is known that the type of objects in array returned by addresses property of NSNetService class is NSData.
Enumerate all elements of sender.addresses array (sender constant of NSNetService type is a service for which addresses are resolved) and perform the following:
1. Declare two variables: inetAddress of sockaddr_in! type and inetAddress6 of sockaddr_in6! type for IPv4 and IPv6 addresses correspondingly. Both variables are implicitly unwrapped optionals.
sockaddr_in structure describes the socket for work with TCP/IP protocols and has the following form:
struct sockaddr_in { var sin_len: __uint8_t var sin_family: sa_family_t var sin_port: in_port_t var sin_addr: in_addr var sin_zero: (Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8) }
The structure properties:
sin_len - structure length;
sin_family - address family (must be equal to AF_INET);
sin_port - port of IP addresses;
sin_addr - IP address;
sin_zero - padding.
For IPv6 address representation, sockaddr_in6 structure is used. sin6_family property stores address family (must be equal to AF_INET6), and sin6_addr property stores IPv6 address.
2. Declare inetAddressPointer variable and assign constant pointer to sockaddr_in structure to it.
bytes property of NSData class returns pointer of UnsafePointer<()> type (analog in Objective-C is const void *) to data of object of this class:
var bytes: UnsafePointer<()> { get }
As it is known that the object contains sockaddr structure, we can cast returned value to UnsafePointer type.
3. Assign to inetAddress variable the value which is stored in inetAddressPointer pointer.
To get the object stored in UnsafePointer structure, we'll use its memory property:
var memory: T { get }
4. Check address family. Note that during address family check implicit type casting is carried out (AF_INET – Int32 type, while the type of sin_family property of sockaddr_in structure is sa_family_t (alias for __uint8_t type). Omitting type casting leads to compilation error.
In case the address family is AF_INET (IPv4 address), no additional actions are required. Otherwise, check whether AF_INET6 (IPv6) is the address family. If it really is, then assign constant pointer to sockaddr_in6 structure to inetAddressPointer6 variable, the structure stored in this address to inetAddress6 variable, and nil to inetAddress variable.If not, assign nil to inetAddress variable.
5. Declare ipString variable of UnsafePointer? optional type. Question mark means that the variable value is optional, that is, the variable can contain constant pointer to Int8 type or can contain no values at all.
6. Declare ipStringBuffer variable of UnsafeMutablePointer type (pointer to Int8 type) and allocate memory via alloc method.
7. As there is a possibility that inetAddress variable became equal to nil, process it as optional variable to check whether it contains any value. If it does, declare addr variable and assign to it the address stored in sin_addr field of inetAddress structure. Then assign constant pointer to the string returned by inet_ntop function to ipString variable.
inet_ntop function converts a packed Internet address into the readable format. The function gets binary IP address and returns the pointer to the string which contains decimal format with dots and colons.
variable of Int32 type - address family (sin_family variable of inetAddress structure should be casted to Int32 type, that is Int32(inetAddress.sin_family));
constant pointer of UnsafePointer type. Here’s an extract from Apple's doc "Using Swift with Cocoa and Objective-C. Interacting with C APIs":
When a function is declared as taking a UnsafePointer argument, it can accept the same operands as UnsafePointer for any type Type.
That's why, we can pass the value of UnsafePointer type to the function as the second argument, that is, constant pointer to in_addr structure which stores IP address.
variable of UnsafeMutablePointer type - pointer to buffer;
variable of socklen_t type - buffer size (cast INET6_ADDRSTRLEN constant of Int32 type to __uint32_t type (socklen_t type is alias for __uint32_t)).
Underscore (_) means that you don't have to indicate outer names for the parameters at function call.
In case inetAddress variable does not contain value, check whether inetAddress6 variable contains any value. If it does, declare addr variable and assign to it the address which is stored in sin6_addr field of inetAddress6 structure. Then assign constant pointer to the string returned by inet_ntop function to ipString variable.
8. Then check whether variable ipString contains value. If it does, declare ip variable and assign it the value returned by fromCString static method of String structure.
Since ipString variable is of UnsafePointer? optional type, one needs to force unpack it (exclamation mark after the variable name) before passing it to fromCString method. Casting UnsafePointer type to UnsafePointer type is not necessary, as CChar is alias for Int8.
If ip variable contains the value, declare ext variable of String type and assign to it the value depending on the service type. Note that in switch-case construction there is no break operator in case-blocks, as, unlike switches in C and Objective-C, switches in Swift do not pass to the next block by default after executing code inside the corresponding case-block.
Add to sharedPointsList array the object of SharedPoint class, which is created via initializer with three named parameters. As ip variable is of String? type, that is, optional String, we'll force unpack it.
After that, send to the notification center of the process the notification that a new point with file sharing was found.
9. Since UnsafePointer structure does not provide automatic memory control, free the memory allocated to ipStringBuffer variable.
Add implementation of one more method of the NSNetServiceDelegate protocol to the SharedBrowser class.
netServiceDidResolveAddress(_,didNotResolve:) method informs the delegate that an error occurred during resolution of a given service.
When calling this method, a message with code error will be shown in console. One can get an error code via NSNetServiceErrorCode key from errorDict dictionary.
func netService(sender: NSNetService, didNotResolve errorDict: [NSObject : AnyObject]) { NSLog("\(sender.name) did not resolve: \(errorDict[NSNetServicesErrorCode]!)") }
All is left is to add to the class SharedBrowser method which is intended to launch network search for computers with enabled file sharing. In our case, there are three search variants available:
search for computers with file sharing via AFP protocol;
search for computers with file sharing via SMB protocol;
search for computers with file sharing via AFP and SMB protocols.
To differentiate the three search variants, we'll create ServiceType enumeration with three members, each of which corresponds to the definite search variant:
enum ServiceType { case Afp case Smb case AfpAndSmb }
Note that, unlike in C and Objective-C, integer values cannot be assigned by default to enumeration members at their creation in Swift. Afp, Smb and AfpAndSmb members in ServiceType enumeration are not equal to 0, 1 and 2 correspondingly, but they are actual values.
Add the following method to SharedBrowser class:
func searchForServicesOfType(type:ServiceType) { switch type { case .Afp: afpBrowser.searchForServicesOfType(afpType, inDomain: "") case .Smb: smbBrowser.searchForServicesOfType(smbType, inDomain: "") case .AfpAndSmb: afpBrowser.searchForServicesOfType(afpType, inDomain: "") smbBrowser.searchForServicesOfType(smbType, inDomain: "") } }
The method accepts the only parameter of ServiceType type, describing the search variant. In switch-case construction, the short form of access to ServiceType enumeration members is used, like .Afp, not ServiceType.Afp, since it is known that the variable type is ServiceType.
searchForServicesOfType(_,inDomain:) method of NSNetServiceBrowser class launches the search for services of the definite type in the specified domain.
Passing an empty string to the method as the second parameter lets the browser search for services among registration domains used by default.
Add to SharedBrowser class the last method which is intended to stop the current search and clear its results:
func reset() { smbBrowser.stop() afpBrowser.stop() for service in serviceList { service.stop() } serviceList.removeAll() sharedPointsList.removeAll() }
Now let's start the creation of the user interface.
User Interface Creation
1. Choose MainMenu.xib file in project browser to display SharingBrowser window.
2. Display the Utilities in the right corner of the window. To do this, click the rightmost button of View switch on the toolbar.
3. Display the main window. To do this, click Window icon in Editor.
4. Open the Object Library. The Object Library can be found in the lower part of the Utilities.
5. Drag Push Button, Table View, two Check Boxes and Box to the window from the Object Library.
6. Place the objects as shown on the picture and name them appropriately.
7. In Attributes inspector tab, choose Content Mode in Cell Based, and set the number of Columns to 4.
8. For each column of the table set the name in Attributes inspector tab. In Identity inspector tab, set the Restoration ID: Name and name correspondingly for the 1rst column, Domain and domain - for the 2nd column, Type and type - for the 3rd column, IP and ip - for the 4th column.
9. Create outlet for each element in AppDelegate class. To do this, display Assistant editor panel by clicking Editor switch button
on the toolbar. Make sure that Assistant editor displays AppDelegate.swift file. If not, choose AppDelegate.swift file from Top Level Objects menu in the upper part of the panel.
For each element, do the following:
Click Control key and hold it, while dragging element to AppDelegate.swift file;
Release Control key and Assistant editor will display connection setup window;
Choose Outlet from Connection menu;
Input the name and click Connect button.
Interface Builder will add the outlet declaration to the class. Outlets in Swift are declared via the @IBOutlet key word.
The following strings should appear in AppDelegate class:
@IBOutlet weak var window: NSWindow! @IBOutlet weak var sharedPointsTable: NSTableView! @IBOutlet weak var afpCheckBox: NSButton! @IBOutlet weak var smbCheckBox: NSButton! @IBOutlet weak var searchBtn: NSButton!
10. Set AppDelegate object as the data source for the table. To do this, click Control key and hold it, while dragging the pointer from the table towards AppDelegate object in the objects hierarchy.
Choose dataSource property.
11. Add action for Search button. Click Control key and hold it, while dragging Search button into AppDelegate.swift file. Release Control key, choose Action from Connection menu, input searchBtnAction as the action name. Interface Builder will add the stub for the method to AppDelegate class:
First, declare searchType variable of ServiceType? type, that is, optional ServiceType type. Make sure that afpCheckBox checkbox is ticked. If it is, make sure that smbCheckBox checkbox is ticked as well. If yes, assign .AfpAndSmb value to searchType, otherwise - .Afp.
If afpCheckBox checkbox is not ticked, make sure that smbCheckBox checkbox is ticked. If yes, assign .Smb value to searchType. Check whether searchType variable contains value. If it does, stop the previous search, clear its results and srart the new search. If not, display the warning window.
To populate the table with content programmatically, NSTableViewDataSource protocol should be realized. Since AppDelegate class was set in Interface Builder as the data source for the table, add the NSTableViewDataSource protocol conformance to it:
class AppDelegate: NSObject, NSApplicationDelegate, NSTableViewDataSource { // class definition goes here }
numberOfRowsInTableView(_:) method of NSTableViewDataSource protocol returns the number of records provided to NSTableView object by the data source.
Method declaration:
optional func numberOfRowsInTableView(_ aTableView: NSTableView) -> Int
Add this method implementation to the AppDelegate class:
func numberOfRowsInTableView(tableView: NSTableView!) -> Int { return sharedBrowser.sharedPointsList.count }
tableView(_:objectValueForTableColumn:row:) method of the NSTableViewDataSource protocol is called by the table to return the data of the object, related to the indicated row and column.
Add this method implementation to the AppDelegate class:
func tableView(tableView: NSTableView, objectValueForTableColumn tableColumn: NSTableColumn?, row: Int) -> AnyObject? { if row < 0 || row >= sharedBrowser.sharedPointsList.count { return "" } if let columnId = tableColumn?.identifier { let sharedPoint = sharedBrowser.sharedPointsList[row] if columnId == "name" { return sharedPoint.netService.name } else if columnId == "domain" { return sharedPoint.netService.domain } else if columnId == "type" { return sharedPoint.type } else if columnId == "ip" { return sharedPoint.ipAddress } } return "" }
In applicationDidFinishLaunching(_:) method set the AppDelegate class itself as the target for the sharedPointsTable, and set the selector for its doubleAction property. At double-click on the table's row, mountToShare(sender:) method will be called, which is indented to create the connection to file sharing service, corresponding to the row. In this very method subscribe to NewSharedDetected notification, upon receipt of which reloadSharedPointsTable(), method will be called, which is indented to refresh sharedPointsTable table data.
func applicationDidFinishLaunching(aNotification: NSNotification) { // Insert code here to initialize your application sharedPointsTable.target = self sharedPointsTable.doubleAction = "mountToShare:" NSNotificationCenter.defaultCenter().addObserver(self,selector: "reloadSharedPointsTable", name: "NewSharedDetected", object: sharedBrowser) }
func mountToShare(sender:NSTableView!) { var clickedRow = sender.clickedRow if (clickedRow < 0) || (clickedRow > (sharedBrowser.sharedPointsList.count - 1)) { return } let sharedPoint = sharedBrowser.sharedPointsList.objectAtIndex[clickedRow] let ip = sharedPoint.ipAddress if let a = NSAppleScript(source: "tell application \"Finder\"\ntry\nmount volume \ "\(sharedPoint.type)://\(ip)\"\nend try\nend tell\n") { var errorInfo: NSDictionary? if a.compileAndReturnError(&errorInfo) { if (a.executeAndReturnError(&errorInfo) != nil) { // success } } else { } if errorInfo != nil { NSLog("Error %@",errorInfo!); } } }
Connection to file sharing service is processed via script on AppleScript. There's NSAppleScript class to create and perform scripts in Cocoa applications.
NSAppleScript object creation is performed via init?(source: String) initializer, to which a string with script code is passed.
Application Launch
1. Launch the app by clicking Run button.
2. Click Search button.
3. In the table, you will see computers' names with enabled file sharing, the protocol type used for sharing and their IP addresses.
4. To connect to a definite computer with enabled file sharing, double-click the row corresponding to it. You’ll see the window:
Congrats! That's your first Swift app!
Download the archive with sources of the above described sample here: https://dl.dropboxusercontent.com/u/2191286/SharingBrowser.zip
Interacting with C Pointers in Swift. Part 3: using CFunctionPointer
Hi there!
In one of my previous posts (see Interacting with C Pointers in Swift. Part 2), I told you how the work with pointers in Swift is organized, and I also mentioned CFunctionPointer type. Let's scrutinize this type today and see why we actually need it.
In “Using Swift with Cocoa and Objective-C. Interacting with C APIs” doc, CFunctionPointer type is described in few words only:
C function pointers are imported into Swift as CFunctionPointer, where Type is a Swift function type. For example, a function pointer that has the type int (*)(void) in C is imported into Swift asCFunctionPointer<() -> Int32>.
The same way as pointers to variables let us get values of variables they are pointing to, pointers to functions let calling the corresponding functions.
int getNextRandomValue() { srand(time(NULL)); int randomNumber = rand() % 100 + 1; printf("New random number: %i\n",randomNumber); return randomNumber; }
The pointer to this function in C would have int (*)(void) type, while in Swift it will have CFunctionPointer<() -> Int32> type. To create CFunctionPointer, COpaquePointer is needed, e.g.:
let pointer = UnsafeMutablePointer<() -> Int32>.alloc(1) pointer.initialize(getNextRandomValue) let cPointer = COpaquePointer(pointer) let functionPointer = CFunctionPointer<() -> Int32>(cPointer)
To call a function via a pointer to it, do the following:
let newCPointer = COpaquePointer(functionPointer) let newPointer = UnsafeMutablePointer<() -> Int32>(newCPointer) let rNumber = newPointer.memory()
Ok. Now let's turn our eyes to callback functions. Pointers to functions are valued as they can be passed as parameter to other functions. A function passed to another function for calling as pointer is called a callback function. Suppose, we need to track when new disk or partition is created.
Let's create Disk Arbitration session, register DADiskAppearedCallback and pass the object for handling:
func gotDisk(disk: DADisk!, context: UnsafeMutablePointer) { NSLog("Got new disk: \(DADiskGetBSDName(disk))") } … var session = DASessionCreate(kCFAllocatorDefault).takeRetainedValue() let p = UnsafeMutablePointer<((DADisk!, UnsafeMutablePointer) -> Void)>.alloc(1) p.initialize(gotDisk) let cp = COpaquePointer(p) let fp = DADiskAppearedCallback(cp) DARegisterDiskAppearedCallback( session, kDADiskDescriptionMatchVolumeMountable.takeRetainedValue(), fp, nil) /* Schedule a disk arbitration session. */ DASessionScheduleWithRunLoop(session, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode) ...
On handling stage we will get EXC_BAD_ACCESS exception.
Let's see what caused the exception. We'll create our own C-function, which takes pointer of void f(void) type:
void executeFunction(void(*f)(void)) { f(); }
Call this function from Swift, passing a pointer to greeting function as parameter:
func greeting() { NSLog("Hello from Swift") } ... let p = UnsafeMutablePointer<()->()>.alloc(1) p.initialize(greeting) let cp = COpaquePointer(p) let fp = CFunctionPointer<()->()>(cp) executeFunction(fp) ...
On handling stage we'll get EXC_BAD_ACCESS exception again.
In debugger we see that instead of greeting function address, CFunctionPointer address is returned to executeFunction method:
Thus, it seems to be impossible to call a function via CFunctionPointer.
Hope this problem will be resolved in the nearest future. I'll keep you updated.
Anya is live and ready to show you everything. Watch her strip, dance, and perform exclusive shows just for you. Interact in real-time and make your fantasies come true.
✓ Live Streaming✓ Interactive Chat✓ Private Shows✓ HD Quality
Anya is LIVE right now
FREE
Free to watch • No registration required • HD streaming
Convert NSMutableArray to Swift Array (Array[Type])
Hi there. Here's one more tiny but helpful tip from my Swift experience.
In my relatively big Swift project I have to use Foundation framework classes, which sometimes causes certain issues. Today I'll tell you about one of those issues and the solution to it.
Suppose we have a variable of NSMutableArray type and we need to convert it into, say, Swift array with strings. Apparently the code should look like:
var nsArray = NSMutableArray() ... var swiftArray = nsArray as [String]
However, the compiler won't accept it:
'String' is not identical to 'AnyObject'
To resolve this, write the code as:
var nsArray = NSMutableArray() ... var swiftArray = nsArray as AnyObject as [String]
Voila! Now we can work with such arrays like with any ordinary Swift array.
The same issue occurs (and the same solution can be applied) when converting NS(Mutable)Dictionary into Swift dictionary.
In my previous post I told you about the fix for compilation error in case optimization was enabled. And as I've already mentioned, I still haven't succeeded in improving app’s performance. Today I'll tell you how I found the reason for the poor performance and how I fixed it.
In my project, the list of files sorted by name should be displayed. When lists are sorted via string comparator, the performance is good enough. But if the files are sorted like in the Finder, performance drops dramatically in case array contains a big amount of elements.
For example, let's take the contents of ~/Library/Preferences folder (1000 files in total) and sort the list with this code:
filesArray.sort({ (item1:NSURL, item2:NSURL) -> Bool in let comparisonOptions:NSStringCompareOptions = NSStringCompareOptions.CaseInsensitiveSearch | NSStringCompareOptions.NumericSearch | NSStringCompareOptions.WidthInsensitiveSearch | NSStringCompareOptions.ForcedOrderingSearch; var str1:String = item1.path!.lastPathComponent var str2:String = item2.path!.lastPathComponent var res = str1.compare(str2, options: comparisonOptions, range: str1.startIndex ..< str1.endIndex, locale: NSLocale.currentLocale()) return res == NSComparisonResult.OrderedAscending })
When using Xcode6 beta6 with the enabled optimization, it takes 5 seconds to sort the files. (Just to compare, it took about several minutes in beta5.) As you see, files sorting speed cannot be called acceptable anyway.
I decided to compare the sorting time of the method described above with the time of this method: