Swift: Regular expression-based string replacements

One of the quirkiest things in Swift for me turned out to be the understanding on how the Strings work.

Since all the Strings are Unicode in Swift, it is the fact that sometimes a character might be constructed from two (or more) unicode glyphs what makes the String manipulation a bit tricky.

The root of all string operations is the understanding of what the String index is and how does it differs from, say Int.

The String index is basically a representation of the character’s position within the String. Swift differs from many of the other programming languages by the fact that it’s Strings are actually CollectionTypes (instead of being treated as an array of characters or an indexed pointer) and the String index is BidirectionalIndexType instead of being an Int that is used to access a character within the string in the other programming languages. This gives us the ability to have functions like predecessor() or successor() on string indexes among everything else.

In short you can’t get a character by using string[3]. To access a character in Swift you have to use a String index, which is generated by the Core library’s advance function:

var s = "test"
var charIndex = advance(s.startIndex, 2)
var ch = s[charIndex]
>> "s"
ch = s[charIndex.predecessor()]
>> "e"
ch = s[charIndex.successor()]
>> "t"

Almost any of the string manipulations are performed using ranges. Ranges must consist of String indexes. A numeric range will produce an error:

charIndex = advance(s.startIndex, 2)

s.substringWithRange(Range(start: charIndex, end: s.endIndex))
// or
s[charIndex..<s.endIndex]
>> "st"

s.replaceRange(Range(start: charIndex, end: s.endIndex), with: "rererer")
// or
s.replaceRange(charIndex..<s.endIndex, with: "rererer")
>> "terererer"

Now, let’s give some practical usage to that knowledge.

Let’s assume that we have a string that has some HTML data and we want to convert that string to a Markdown-compatible one. For the sake of simplicity I’ll just look at the case of replacing the <strong> opening and closing tags with * which is used to make a string bold using Markdown. See below:

var s = "<strong>Hell</strong>o, <strong>Hell</strong>o, <strong>Hell</strong>o"
var search = "<\\/?strong>"
var replaceWith = "*"
var replacementLength = countElements(replaceWith)
var err: NSError? = nil
var expr = NSRegularExpression(pattern: search, options: .CaseInsensitive, error: &err)


if let matches = expr?.matchesInString(s, options: nil, range: NSMakeRange(0, countElements(s)) ) {
  var replacedStringLengthDifference = 0
  for match in matches {
    var startIndex = advance(s.startIndex, (match.range.location + replacedStringLengthDifference))
    var endIndex = advance(s.startIndex, (match.range.length + match.range.location + replacedStringLengthDifference))
    replacedStringLengthDifference -= (match.range.length - replacementLength)
    s.replaceRange(startIndex..<endIndex, with: replaceWith)
  }
}

>> "*Hell*o, *Hell*o, *Hell*o"

Leveraging the string indexes and ranges it’s pretty easy to do String manipulations in Swift. The replacedStringLengthDifference is a variable that keeps the difference in the string length (amount of characters in Int) after the replacement has been made. We use that difference to find the correct location of the next match.

Of course, all of the above could be solved by using a regular expression-based replacement, it depends if you want to have the source string replaced, in which case you’ll have to turn the string into a NSMutableString first, or if you want a new string that contains the replacements. Here’s the case where we replace within the original string:

var original = NSMutableString(string: "<strong>Hell</strong>o, <strong>Hell</strong>o, <strong>Hell</strong>o")
var search = "<\\/?strong>"
var replaceWith = "*"
var err: NSError? = nil
var expr = NSRegularExpression(pattern: search, options: .CaseInsensitive, error: &err)
if (err === nil) { 
  expr?.replaceMatchesInString(original, options: nil, range: NSMakeRange(0, original.length), withTemplate: replaceWith)
  println(original)
}

>> "*Hell*o, *Hell*o, *Hell*o"

And here’s a case where we get a string containing all the replacements by keeping the original string untouched. Note that the replacement is an Optional:

var original = "<strong>Hell</strong>o, <strong>Hell</strong>o, <strong>Hell</strong>o"
var search = "<\\/?strong>"
var replaceWith = "*"
var err: NSError? = nil
var expr = NSRegularExpression(pattern: search, options: .CaseInsensitive, error: &err)
if (err === nil) { 
  if let replacement = expr?.stringByReplacingMatchesInString(original, options: nil, range: NSMakeRange(0, countElements(original)), withTemplate: replaceWith) {
    println(replacement)
  }
}

>> "*Hell*o, *Hell*o, *Hell*o"

Swift: Printing an Array of enum

It was just recently that I discovered a cool little feature that allows you to describe Objects, structs and enums in Swift. It’s called the Printable protocol and it’s part of the Swift’s standard library.

Let’s describe a situation when it is handy. Imagine that you have an array that represents a magazine table of contents. This array holds not only the articles but also the adverts between them.

Let’s assume that our articles will all have a title, as well as many other properties that I won’t list for the sake of simplicity:

class Article {
  var title =  ""
  ...
  init (title:String) {
    self.title = title
  }
}

Let’s also assume that our adverts will all have a description:

class Advertising {
  var description = ""
  ...
  init(description:String)
  {
    self.description = description
  }
}

Now, we need an array which will host instances of both Article and Advertising type. Since Swift arrays are typed we have to base our array on an enum that has associated values to achieve our goal:

enum Content {
  case Story(Article)
  case Ad(Advertising)
}

var articles = [Content]()
var article1 = Article(title: "First article")
var ad1 = Advertising(description: "Some advertising")
var article2 = Article(title: "Second article")

articles += [Content.Story(article1), Content.Ad(ad1), Content.Story(article2)]

So, what’s the problem?

Well, sometimes we have to have a peak at what’s in the array and when we try to do so we usually get a very obscured output:

println(articles)
>>  [(Enum Value), (Enum Value), (Enum Value)]

OK, what can I do about it?

Here’s where the Printable protocol comes into play. Let’s look at what we have to do to get it implemented:

/// A type with a customized textual representation.
///
/// This textual representation is used when objects are written to an
/// *output stream*, for example, by `print` and `println`.
protocol Printable {

  /// A textual representation of `self`.
  var description: String { get }
}

Pretty easy to understand, huh? Probably one of the easiest protocols (if not the easiest one) in Swift.

Hint: Have a peak at the Apple’s documentation for an example on how to use it with structs: https://developer.apple.com/library/ios/documentation/General/Reference/SwiftStandardLibraryReference/Printable.html

The Solution

Let’s say that we want to display the article’s title and the advertising’s description when we print out the articles array.

To achieve that we have to extend our enum a bit, in a way that it implements the Printable protocol. Here’s one way to do it:

enum Content: Printable {
  case Story(Article)
  case Ad(Advertising)

  var description: String {
    get {
      switch(self){
        case let .Story(article):
          return "\"\(article.title)\""
        case let .Ad(advertising):
          return "\"\(advertising.description)\""
      }
    }
  }
}

Doing so allows us to get what we want – the article’s title and the advertising’s description are displayed when we print out the articles array, replacing the obscure (Enum value):

println(articles)
>> ["First article", "Some advertising", "Second article"]

Note: At the moment of writing the Printable protocol doesn’t work in the playground (Xcode 6.1.1), which is a known bug and hopefully will be sorted out soon. The code works great in a project context though 🙂

2014 in Music

2014 се оказа доста силна година – сред мейнстрийма нямаше нищо ново, но underground-a е пълен с интересни находки като Intervals, Monuments и Animals As Leaders. Особено внимание заслужават албумите на Coldplay и Royal Blood – Първият, защото става за всякакви ситуации, а вторият – защото е записан от група, състояща се само от барабанист и басист. Има и неща от чиста проба уважение (AC/DC, Pink Floyd, Slash), както и доста неща останали извън 20-ката (Machine Head, Machinae Supremacy), които спокойно могат да ги заместят, така че не мога да съм категоричен че ако бях писал поста по друго време нямаше да изглежда по-различно.

В крайна сметка – ето 20 албума, които можете да си завъртите и да прецените за себе си стават ли, или не.

2014 се оказа доста силна година – сред мейнстрийма нямаше нищо ново, но underground-a е пълен с интересни находки като Intervals, Monuments и Animals As Leaders. Особено внимание заслужават албумите на Coldplay и Royal Blood – Първият, защото става за всякакви ситуации, а вторият – защото е записан от група, състояща се само от барабанист и басист. Има и неща от чиста проба уважение (AC/DC, Pink Floyd, Slash), както и доста неща останали извън 20-ката (Machine Head, Machinae Supremacy), които спокойно могат да ги заместят, така че не мога да съм категоричен че ако бях писал поста по друго време нямаше да изглежда по-различно.

В крайна сметка – ето 20 албума, които можете да си завъртите и да прецените за себе си стават ли, или не.

20
World Of Fire
Slash
19
Vices. Virtues. Visions.
Raunchy
18
Catacombs Of The Black Vatican
Black Label Society
17
V
Voyager
16
California Breed
California Breed
15
Ruining Lives
Prong
14
The Endless River
Pink Floyd
13
Rock Or Burst
AC/DC
12
Second Nature
Flying Colors
11
Hymns For The Broken
Evergrey
10
Distant Satellites
Anathema
09
Clear
Periphery
08
Royal Blood
Royal Blood
07
Sonic Highways
Foo Fighters
06
Synaesthesia
Synaesthesia
05
Ghost Stories
Coldplay
04
The Amanuensis
Monuments
03
Pale Communion
Opeth
02
A Voice Within
Intervals
01
The Joy Of Motion
Animals As Leaders

2013 in music

Малко странен ми се вижда фактът, че оценявам изкуство, затова предпочитам да гледам на „класацията“ като на списък с 20-те неща които ми направиха най-силно впечатление през годината и подредбата им отразява по-скоро моментното ми отношение спрямо тях.

Малко странен ми се вижда фактът, че оценявам изкуство, затова предпочитам да гледам на „класацията“ като на списък с 20-те неща които ми направиха най-силно впечатление през годината и подредбата им отразява по-скоро моментното ми отношение спрямо тях.

20
The Living Infinite
Soilwork
19
Brief Nocturnes and Dreamless Sleep
Spock’s Beard
18
Like Clockwork
Queens Of The Stone Age
17
Random Access Memories
Daft Punk
16
The Devil Put Dinosaurs In Here
Alice In Chains
15
Eternal Movement
Tides From Nebula
14
House of Gold & Bones Part 2
Stone Sour
13
The Theory Of Everything
Ayreon
12
Earth Rocker
Clutch
11
Unstoppable Momentum
Joe Satriani
10
Culture Clash
The Aristocrats
09
Dream Theater
Dream Theater
08
Desolation Rose
The Flower Kings
07
Sempiternal
Bring Me The Horizon
06
Shrine Of New Generation Slaves
Riverside
05
Darkness In Different Light
Fates Warning
04
The Raven That Refused to Sing
Steven Wilson
03
Hail To The King
Avenged Sevenfold
02
Fortress
Alter Bridge
01
Altered State
Tesseract

2012 in music

С леко закъснение – ето я и 2012 така, както я чух

С леко закъснение – ето я и 2012 така, както я чух

10
Story Of Light
Steve Vai
09
Weather Systems
Anathema
08
Fire Make Thunder
OSI
07
I Am Anonymous
Headspace
06
Flying Colors
Flying Colors
05
Silverthorn
Kamelot
04
House of Gold & Bones Part 1
Stone Sour
03
The 2nd Law
Muse
02
Storm Corrosion
Storm Corrosion
01
Periphery II: This Time It’s Personal
Periphery

2011 in music

Този път заглавията са 30 и, както винаги, няма празно. Годината беше пълна с хубава музика, прекрасни концерти и списъкът можеше да удари 50 без никакви проблеми 🙂

Този път заглавията са 30 и, както винаги, няма празно. Годината беше пълна с хубава музика, прекрасни концерти и списъкът можеше да удари 50 без никакви проблеми 🙂

30
Testimony 2
Neal Morse
29
Sympathetic Resonance
Arch/Matheos
28
Rockaholic
Warrant
27
Dualism
Textures
26
Eclipse
Journey
25
Velociraptor
Kasabian
24
How I Go
Kenny Wayne Shepherd
23
The Haunting
Within Temptation
22
Evanescence
Evanescence
21
Visions
Haken
20
Impressions
Lunatic Soul
19
Unto The Locust
Machine Head
18
The Road
Mike And The Mechanics
17
Iconoclast
Symphony X
16
The Path Of Totality
Korn
15
Earthshine
Tides From Nebula
14
Bilateral
Leprous
13
Road Salt Two
Pain Of Salvation
12
Sound Of A Playground Fading
In Flames
11
Communication Lost
Wolverine
10
Grace For Drowning
Steven Wilson
09
Anything But Time
Matt Schofield
08
III
Chickenfoot
07
Oceania
Derek Sherinian
06
A Dramatic Turn Of Events
Dream Theater
05
This Mortal Coil
Redemption
04
Worship Music
Anthrax
03
Heritage
Opeth
02
2
Black Country Communion
01
Dust Bowl
Joe Bonamassa

2010 in music

Обикновено около коледа пускам нещо като лична класация съдържаща 10-те ми любими албума, появили се през годината, и, за пореден път, няма да наруша традицията си – напук на клишетата 🙂

Разликата, обаче, е, че албумите тази година са 20! Не ми се струва честно да оставя някои албуми извън 10-ката, без дори да спомена за тях, особено след като през последната година нямах много време да блогвам.

Та – за ваше (не)удоволствие – списъкът започва от 20

Обикновено около коледа пускам нещо като лична класация съдържаща 10-те ми любими албума, появили се през годината, и, за пореден път, няма да наруша традицията си – напук на клишетата 🙂

Разликата, обаче, е, че албумите тази година са 20! Не ми се струва честно да оставя някои албуми извън 10-ката, без дори да спомена за тях, особено след като през последната година нямах много време да блогвам.

Та – за ваше (не)удоволствие – списъкът започва от 20:

20
Road Salt One
Pain Of Salvation
19
Audio Secrecy
Stone Sour
18
Festival
Jon Oliva’s Pain
17
A View From The End Of The World
Machinae Supremacy
16
The Showdown
Allen/Lande
15
We’re Here Beacuse We’re Here
Anathema
14
Fever
Bullet For My Valentine
13
X
Spocks Beard
12
Dead New World
Ill Nino
11
The Obsidian Conspiracy
Nevermore
10
Poetry For The Poisoned
Kamelot
09
Sitha Ahra
Therion
08
Avalon
Sully Erna
07
Lunatic Soul II
Lunatic Soul
06
Static Impulse
James LaBrie
05
7 Sinners
Helloween
04
Plastic Pig Society
Bastardolomey
03
Victims Of The Modern Age
Star One
02
Strings To A Web
Rage
01
Nightmare
Avenged Sevenfold

TightMVC

От доста време базирам всичките си проекти на MVC PHP frameworks. Наскоро обаче се натъкнах на един проблем – почти няма framework, който да поддържа само основните неща – дори любимият ми CodeIgniter е тлъстичък, когато стане въпрос за малки сайтове – макар да не се използват по подразбиране, библиотеките му си седят в дистрибуцията и заемат ненужно (според мен) място. Да не говорим какъв слон е ZendFramework, въпреки всичките му предимства.

За някои от проектите си наистина имах нужда от нещо доста по-леко, възможно най-минимална имаплементация на MVC pattern-a. Преди време бях попадал на една много добра статия от Anand Garg в която той даваше много добри съвети (и код) за разработване на собствен MVC framework.

От доста време базирам всичките си проекти на MVC PHP frameworks. Наскоро обаче се натъкнах на един проблем – почти няма framework, който да поддържа само основните неща – дори любимият ми CodeIgniter е тлъстичък, когато стане въпрос за малки сайтове. Mакар да не се използват по подразбиране, библиотеките му си седят в дистрибуцията и заемат ненужно (според мен) място. Да не говорим какъв слон е ZendFramework, въпреки всичките му предимства.

За някои от проектите си наистина имах нужда от нещо доста по-леко, възможно най-минимална имаплементация на MVC pattern-a. Преди време бях попадал на една много добра статия от Anand Garg в която той даваше много добри съвети (и код) за разработване на собствен MVC framework.

Започнах от неговия код, целейки само и единствено поддръжка на PHP5. Изхвърлих всичко ненужно, добавих поддръжка на PDO за комуникация с базата данни, добавих поддръжка за Layouts, Helpers и Modules и в крайна сметка получих един доста приличен framework с много малък codebase.

Резултатът е качен тук, а не много пълна документация можете да откриете във wiki-то към проекта тук.

На който му се стори интересно – може да тегли, тества, праща bug reports и т.н.

To Smoke or not to?

Напоследък е супер модерно от всички медии да се тръбят всякакви апокалиптични сценарии свързани с все по-рестриктивните за пушещата част от населението реформи. За да ми схванете мисълта по-ясно ще поясня – пушач съм от 10-15 години, средното количество цигари, които съм изпушвал дневно никога не е минавало повече от 10, а за последната година имам около три пъти и половина пъти повече дни като непушач от тези в които съм палил цигара. Накратко: познавам аргументите и на двете страни.

Напоследък е супер модерно от всички медии да се тръбят всякакви апокалиптични сценарии свързани с все по-рестриктивните за пушещата част от населението реформи. За да ми схванете мисълта по-ясно ще поясня – пушач съм от 10-15 години, средното количество цигари, които съм изпушвал дневно никога не е минавало повече от 10, а за последната година имам около три пъти и половина пъти повече дни като непушач от тези в които съм палил цигара. Накратко: познавам аргументите и на двете страни.

Да, пушачите са прави – никой не може да им налага забрани, че и глоби при положение, че не е финансирал нито отказването на цигарите, нито ще ги компенсира по някакъв друг начин, а допреди само 5 години дебат за/против тютюнопушенето на обществени места звучеше като “абе чешем си езиците тука”. Непушачите също са прави – искат хората да дишат въздух, а не миризливите кълба дим на човека от съседната маса. Преди 5 години дори не можех да си представя, че ще се водят дискусии по въпроса. Преди 5 години щях да кажа “да го д**ате” на непушачите, или на пушачите, в зависимост от гледната си точка.

Още не мога да разбера как тоя точно въпрос дойде на дневен ред и регулярно се мъдри на челно място в медиите, заемайки място и смъквайки вниманието ни от поредния проблем с някоя от магистралите, поредната отложена реформа, поредното неплащане на държавата към доставчиците, поредните доклади от ЕС, поредната акция на “родната полиция” или поредното интервю на бате Бойко, в което не се казва нищо конкретно.

Единственото с което мога да опрадая интереса към пушачите е, че “тия дават по 4 лева на ден за цигари, ‘що да не ги дават за бюджета?”. Лошото е, че когато нищо не си дал, няма какво да искаш. Не можеш да искаш високи такси, а инфраструктурата и услугите ти да са по-зле от тези на държави с пъти по-слаба икономика. Не можеш да искаш данъци от бизнеса и едновременно с това да го шамариш. Никой не прави така.

Нали се сещате – “цялата Мара втасала…”?

plupload and CodeIgniter 1.7.2

CodeIgniter logo

Ако някога сте ползвали чудесният плъгин за качване на файлове на Moxiecode, plupload, то вероятно сте харесали решението за качване на много файлове наведнъж.

Ако пък сте ползвали CodeIgniter, би трябвало в момента, в който сте погледнали примера в download-натия файл от сайта на plupload, да сте забелязали променливите в URL-a (т.нар. query string). Точно те са причината връзката между CodeIgniter и plupload да не се получава.

CodeIgniter logo

Ако някога сте ползвали чудесният плъгин за качване на файлове на Moxiecode, plupload, то вероятно сте харесали решението за качване на много файлове наведнъж.

Ако пък сте ползвали CodeIgniter, би трябвало в момента, в който сте погледнали примера в download-натия файл от сайта на plupload, да сте забелязали променливите в URL-a (т.нар. query string). Точно те са причината връзката между CodeIgniter и plupload да не се получава.

Аз лично не понасям query_strings, а използването на $_GET параметри, поне според мен, трябва да става в URI. Това е и поведението на CodeIgniter, в документацията на който е написано следното:

GET, POST, and COOKIE Data

GET data is simply disallowed by CodeIgniter since the system utilizes URI segments rather than traditional URL query strings (unless you have the query string option enabled in your config file). The global GET array is unset by the Input class during system initialization.

Накратко, за да си решите проблема с интеграцията на plupload и CodeIgniter ще трябва малко повечко работа.

Първо – plupload изпраща името на файла, който се качва в момента като query string променлива, която би трябвало да получите като $_GET параметър. Input class-ът на CodeIgniter обаче маха целия $_GET, затова ще embed-нем името на файла в URI. Същото ще стане и с двете променливи chunk и chunks. Целта ни е да получим URL от сорта на:

http://example.com/uploader/upload_file/filename.ext/0/4

където 0 и 4 са стойностите съответно на chunk и chunks. Забележете, че тези стойности варират според големината на качвания файл и стойността на конфигурационния параметър chunk_size.

За пример ще взема html5 widget-ът. Малката промяна, която престои да направим, е във plupload.js, в частност във функцията :

buildUrl : function(url, items) {
  var query = '';
  plupload.each(items, function(value, name) {
    query += (query ? '&' : '') + encodeURIComponent(name) + 
    '=' + encodeURIComponent(value);
  });
 
  if (query) {
   url += (url.indexOf('?') > 0 ? '&' : '?') + query;
  }
 
	return url;
}

Крайната цел е да изглежда така:

buildUrl : function(url, items) {
  var query = '';
  plupload.each(items, function(value, name) {
    query += '/' + encodeURIComponent(value);
  });
 
  if (query) {
   url += query;
  }
 
	return url;
}

При това положение достатъчно е в controller-a да имаме следния метод:

<?php
class Uploader extends Controller {
  function Uploader()
  {
    parent::Controller();
  }
 
  function upload_file($fileName, $chunk, $chunks)
  {
    // Validate input
    if ((filter_var($chunk, FILTER_VALIDATE_INT) === TRUE) || 
    (filter_var($chunks, FILTER_VALIDATE_INT) === TRUE))
    {
       die('{"jsonrpc" : "2.0", "error" : {"code": 104, "message": "Incorrect upload data."}, "id" : "id"}');
    }
 
    // HTTP headers for no cache etc
    header('Content-type: text/plain; charset=UTF-8');
    header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
    header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
    header("Cache-Control: no-store, no-cache, must-revalidate");
    header("Cache-Control: post-check=0, pre-check=0", false);
    header("Pragma: no-cache");
 
    // Settings
    $targetDir = './some_target_dir_name';
    $cleanupTargetDir = false; // Remove old files
    $maxFileAge = 60 * 60; // Temp file age in seconds
 
    // 5 minutes execution time
    @set_time_limit(5 * 60);
 
    // Clean the fileName for security reasons
    $fileName = preg_replace ('/_(jpg|jpeg|png|gif)$/i', '.$1', $fileName);
    $fileName = preg_replace ('/[^\w\.\-_]+/', '', $fileName);
 
    // upload.php code from the plupload /example dir follows 
    // (from line 37 on)
  }
}

По този начин CodeIgniter извлича стойностите за chunk и chunks директно от URI-то и plupload започва да работи по очаквания начин. За flash-версията (заради IE и Opera) ще трябва да вземете нещата от src/flash директорията и да преправите src/com/plupload/File.as със следния patch: