Swift

Most olvasom az egész napos híreket, és a legfontosabb hír kimaradt az összes magyar Apple oldalról (mármint nyilván amiket olvasok, azokból ) :).
Ahogy az Apple ígérte, opensource lett a Swift. Nem kis meglepetésemre ma tette elérhetővé. Ez a legjobb névnapi ajándék, amit valaha is adtak :)
Github: https://github.com/apple/swift
Ráadásul rögtön elérhetővé tettek Ubuntun is. Amint lesz időm, egyből kipróbálnom Ubuntun, hogy mire képes!!!

Köszi! Amúgy: Ferenc
Ha nem Ferenc lennék, akkor meg orgona művész :)

Már meg is jelent az első YouTube videó arról, hogy hogyan is lehet használni Ubuntu alatt a swift nyelvet: http://youtu.be/uOepDYkDON0
Már csak egy jó UI library kellene hozzá…

Ha már swift, akkor egy kis kedvcsináló, hogy mire képes a nyelv.

Sok más nyelvvel ellentétben az enum nem csak a típust jelölhet meg, hanem adhatunk hozzá funkciókat, számított tulajdonságokat is. Valamint az egyik nagy erőssége, hogy egy enum meghatározásakor egyedi paramétereket adhatunk meg.
Erre egy példa:

enum Szin {
    case Vilagos, Sotet
}

let vilagosSzin = Szin.Vilagos

switch vilagosSzin {
    case .Vilagos: print("Vilagos színt választottunk")
    case .Sotet:  print("Sötét színt választottunk")
}

Ok, ez az alap kiindulási pont, ez az amit az összes nyelv tud. De a swiftben lehet ilyet is:

enum Szin {
    case Vilagos, Sotet, Sajat(szin: UIColor)
}

let vilagosSzin = Szin.Sajat(szin: UIColor.redColor())

switch vilagosSzin {
    case .Vilagos: print("Vilagos színt választottunk")
    case .Sotet:  print("Sötét színt választottunk")
    case .Sajat(let szin): print("Egyedi színt választottunk: \(szin)")
}

Másik példa egy funkció és egy számított mező hozzáadása:

enum Szam: Int {
    case Nulla = 0, Egy = 1, Ketto = 2

    func plusz(ertek: Int) -> Int {
        return self.rawValue + ertek
    }

    var minuszEgy: Int {
        return self.rawValue - 1
    }
}

let egy = Szam.Egy
let harom = egy.plusz(2)
let nulla = egy.minuszEgy

Ez az utolsó példának nem sok haszna van, de szemléltetésre jó :D

És külön bejegyzést csinálok még egy enum durvulásomnak.
Megpróbálom leírni mit szerettem volna megvalósítani és hogyan jutottam el a megoldásig.

Tehát sokszor előfordul az, hogy storyboard-ból kell betölteni egy UIViewControllert. Ilyenkor szöveg azonosítóval kell meghívni, és előfordulhat könnyen elgépelés. Míg ha ezt enum-ra bízzuk, akkor csak egyszer kell megadni helyesen az azonosítót. Viszont még egy fontos dolog volt, hogy túl sok UIViewController volt már a Main.storyboard fájlban és szerettem volna elszeparálni őket csoportosítva.

Tehát ez az alap kiindulási pont:

if let viewController = self.storyboard?.instantiateViewControllerWithIdentifier("ViewController") as? ViewController {
}

Itt amit meg akarok úszni az az, hogy ha többször kell meghívnom ezt a sort, akkor az idézőjelek közötti értéket ne kézzel kelljen beírni, valamint lustaságból, de ne kelljen már megnézegetni állandóan, hogy milyen azonosítót is adtam a ViewControllernek korábban.

Tehát az első lépés valami ilyesmi volt:

enum Storyboards: String {
    case ViewController = "ViewController", SearchController = "SearchViewController"
}

if let viewController = self.storyboard?.instantiateViewControllerWithIdentifier(Storyboards.ViewController.rawValue) as? ViewController {...}

Nos igen ám ez jó volt így, míg nem találtam ki azt, hogy tegyem külön storyboard-okra a többi kontrollert. A gond az volt, hogy szerettem volna valahogy így megvalósítani:

if let viewController = Storyboards.Main.Viewcontroller.viewController as? ViewController {...}
if let searchController = Storyboards.Main.SearchController.viewController as? SearchViewController {...}

(itt a viewController egy számított tulajdonsága lenne az enum-nak, ami UIViewController?-t ad vissza)

Innen jutottam el oda, hogy enum-on belül csináltam egy enum-ot. Itt már bevezettem egy protocol-t, hogy mindenképpen kell csinálni minden belső enumnak egy viewController számított mezőt, valamint paraméterként be lehet kérni ezt a protocol típust, aminek következtében a kód fogja tudni, hogy valóban van viewController tulajdonsága

protocol StoryboardsType {
	var viewController: UIViewController? { get }
}

enum Storyboards {
	enum Main: String, StoryboardsType {
		case ViewController = "ViewController",
		SearchController = "SearchViewController"

		var viewController: UIViewController? {
			let sb = UIStoryboard(name: "Main", bundle: nil)
			if let vc = sb.instantiateViewControllerWithIdentifier(self.rawValue) as? UIViewController {
				return vc
			}

			return nil
		}
	}

	enum Content: String, StoryboardsType {
		case Main = "ContentMain",
		Details = "ContentDetails",
		List = "ContentList"

		var viewController: UIViewController? {
			let sb = UIStoryboard(name: "Content", bundle: nil)
			if let vc = sb.instantiateViewControllerWithIdentifier(self.rawValue) as? UIViewController {
				return vc
			}

			return nil
		}
	}
}
// igy mar mukodik
if let searchController = Storyboards.Main.SearchController.viewController as? SearchViewController {...}

Na, ez már majdnem jó, csakhogy minden egyes új Storyboard létrehozásakor duplikálni kellene egy “belső” enum-ot, ami nem is lenne nagy baj, csak akkor már háromszor szerepelne a viewController számított tulajdonság a storyboard nevének változása miatt. És ez így nem szép. Ha valamit változtatni kell, akkor mindhárom helyen egyszerre kell változtatnom. Ez így semmiképp sem maradhat. Tehát próbálkoztam tovább. A viewController számított mezőből csináltam egy funkciót a külső enumhoz és kiegészítettem a protocol-t. A protocolon belül a rawValue azért kellett, mert a funkción belül nem tudta a nyelv, hogy az egy RawRepresentable típusú. Hozzá lehetett volna adni a protocol típusához is, de ez így egyszerűbb volt. Ráadásul csak a protocol-ba be kell tenni és nem kell egyáltalán nem kell felülírni semmit sem és működik:

protocol StoryboardsType {
	var storyboardName: String { get }
	var rawValue: String { get }
}

enum Storyboards {
	static func viewController(type: StoryboardsType) -> UIViewController? {
		let storyboardName = type.storyboardName
		let sb = UIStoryboard(name: storyboardName, bundle: nil)
		if let vc = sb.instantiateViewControllerWithIdentifier(type.rawValue) as? UIViewController {
			return vc
		}

		return nil
	}

	enum Main: String, StoryboardsType {
		case ViewController = "ViewController",
		SearchController = "SearchViewController"

		var storyboardName: String {
			return "Main"
		}
	}

	enum Content: String, StoryboardsType {
		case Main = "ContentMain",
		Details = "ContentDetails",
		List = "ContentList"

		var storyboardName: String {
			return "Content"
		}
	}
}

Ennek a hátránya csak annyi, hogy megint nem néz ki szépen a lekérdezés (már megint nem az, amit akarok):

if let searchController = Storyboards.viewController(Storyboards.Main.SearchController) as? SearchViewController {...}

Egy tuti ötlet ennek kijavítására, hogy visszaadtam a protocolnak a viewController tulajdonságot (mert ugyebár ez a cél), Privat tulajdonságot csináltam a külső enum funkciójából és így már a protocol is lehet privát. No meg persze az enumhoz hozzá kellett adnom ismét a viewController számított tulajdonságot. Tehát így néz ki most:

private protocol StoryboardsType {
	var storyboardName: String { get }
	var rawValue: String { get }
	var viewController: UIViewController? { get }
}

enum Storyboards {
	private static func viewController(type: StoryboardsType) -> UIViewController? {
		let storyboardName = type.storyboardName
		let sb = UIStoryboard(name: storyboardName, bundle: nil)
		if let vc = sb.instantiateViewControllerWithIdentifier(type.rawValue) as? UIViewController {
			return vc
		}

		return nil
	}

	enum Main: String, StoryboardsType {
		case ViewController = "ViewController",
		SearchController = "SearchViewController"

		var storyboardName: String {
			return "Main"
		}

		var viewController: UIViewController? {
			return Storyboards.viewController(self)
		}
	}

	enum Content: String, StoryboardsType {
		case Main = "ContentMain",
		Details = "ContentDetails",
		List = "ContentList"

		var storyboardName: String {
			return "Content"
		}

		var viewController: UIViewController? {
			return Storyboards.viewController(self)
		}
	}
}

Egyelőre itt tartok. Már csak egy bajom van, az az, hogy meg kell adni mindenképpen a típusát a lekérdezés után. Jó lenne, ha automatikusan tudná a kód, és nem kellene az “kasztolás” kérdés is.
Bár erre is van ötletem és ha sikerül megoldanom, akkor azt is megírom :D

Na, megvan a megoldás, bár kicsit kényelmetlenebb. Lehet még finomítok majd rajta:

protocol StoryboardsType {
	var storyboardName: String { get }
}

protocol ViewControllerType {
	typealias ViewControllerType: UIViewController
	var identifierName: String { get }
	var viewController: ViewControllerType? { get }
	var storyboardName: String { get }
}

enum Storyboards {
	private static func viewController<T: ViewControllerType>(type: T) -> T.ViewControllerType? {
		let storyboardName = type.storyboardName
		let sb = UIStoryboard(name: storyboardName, bundle: nil)
		if let vc = sb.instantiateViewControllerWithIdentifier(type.identifierName) as? T.ViewControllerType {
			return vc
		}

		return nil
	}

	enum Main: StoryboardsType {
		case Default
		var storyboardName: String { get { return "Main" } }
		enum SearchController: String, ViewControllerType {
			case Default = "Default"
			typealias ViewControllerType = SearchViewController
			var identifierName: String { get { return "SearchViewController" } }
			var storyboardName: String { get { return Storyboards.Main.Default.storyboardName } }
			var viewController: SearchViewController? { get { return Storyboards.viewController(self) } }
		}
	}
}

Na most ennek rengeteg nehézsége van, hiszen a legbelső enum-nál rendesen oda kell figyelni a használt típusokra. Ráadásul be kellett vezetni két helyen is Default értéket, mert különben nem működik (inicializálással kapcsolatban dob hibát). Szóval egy Default szócskával hosszabb lett a végeredmény, viszont nem kell “as?” kaszt, nem kell gondolkodni a ViewController osztályán sem.

if let searchController = Storyboards.Main.SearchController.Default.viewController {...}

És ha mindez nem elég, akkor itt a milliószor egyszerűbb megoldás (jól elmentem az erdőbe…) :D

enum Storyboards {
	private static func viewController(storyboardName: String, identifierName: String) -> UIViewController? {
		let sb = UIStoryboard(name: storyboardName, bundle: nil)
		let vc = sb.instantiateViewControllerWithIdentifier(identifierName)
		return vc as? UIViewController
	}

	enum Main {
		case Default
		var storyboardName: String { get { return "Main" } }

		static var SearchController: SearchViewController? {
			get {
				return Storyboards.viewController(Storyboards.Main.Default.storyboardName, identifierName: "SearchViewController") as? SearchViewController
			}
		}
	}
}

if let searchController = Storyboards.Main.SearchController {...}

Csak idegesített az előzőben az a Default szó :D

Inkább aludnál! :)
(Felemelő lehet a “instantiateViewControllerWithIdentifier” és efféléket többször is begépelni :))))

Ide vagy a swiftbe?
Mert ide copy+paste módszerrel került ide, míg a swiftben (Xcodeban) elkezdem beírni az első pár karaktert és kidobja a lehetőséget. Ráadásul az Xcode elsőként dobja ki az i betűsök közül, ha leggyakrabban az van használatban.
Egyébként megjegyzem, hogy ezt nem iOS-re irtam, hanem OSx-re és ott nincs benne a View szó :) (no meg UIViewController helyett NSViewController van…)

Olvastam a CryptoScript fejlesztőjének egy blogbejegyzését, hogy hogyan is próbálta meg a projektjéből egy library-t készíteni, amit le lehet majd tölteni a Package menedzserrel. Bár igaz azóta sem haladt előre a dolog, de beadott egy bug reportot, amire szépen jöttek is a hozzászólások. Az utolsó előtti az az, hogy valaki egy probléma megoldására csinált egy javítást, és az már elvileg be is került a kódba az Apple ígérete szerint. Tehát tényleg működik a közösség formáló hatása!

ja, a bugreport linkje, ahol ez olvasható: [SR-29] Support custom directory layouts · Issue #5339 · apple/swift-package-manager · GitHub

Úgy látom nem csak engem nem hagyott nyugodni a téma :D
Ma olvastam az Apple RSS között, hogy valaki csinált erre a problémára egy teljesen más megközelítést :D
https://medium.com/swift-programming/uistoryboard-safer-with-enums-protocol-extensions-and-generics-7aad3883b44d#.w2vl7vsqe

Az Apple kiadta a Swift 3.0 előzetesét. Akit érdekel, itt vethet rá egy pillantást:

A változások listája itt nézhető meg:

Végleges verzió valamikor az év vége fele várható.

Szerintem azért adhatta ki most az előzetest, hogy a WWDC-n meg lehessen vitatni dolgokat belőle.

Egyetértek. :)

Egyébként én szeretem nagyon. Az előző commentemben leírt problémával sem volt semmi gond amúgy, mert saját cuccról van szó, szóval könnyen túllendültem rajta :).
Amúgy tudom a legnagyobb baja a namespace hiánya. Valamint a visszamenőleg kompatibilitás hiánya. Bár ez utóbbit úgy tudom, hogy a 4-es verziótól meg fogják oldani. Legalábbis nagyon remélem.
Amúgy legalább most már extension-öknél elfogadja típusként az struct.typealias nevet, a korábbi verzió nekem hibát dobott erre. Szóval nekem már ez is jelentős előrelépés :slight_smile:

Ez a sztori ide illik a legjobban, de félek, hogy topic címe miatt kevesen olvassák el.
Nyilván a cikk eleje a lényeg, halálra röhögtem rajta magam. Bár az is igaz, hogy sokan valószínűleg nem fogják érteni. Mindegy, nekem bearanyozta a napom:
https://medium.com/swift-programming/the-inheritance-curse-3402b9929454

Van egy kérdésem a DispatchQueue-vel kapcsolatban.
Most készítem első SpriteKit alapú alkalmazásomat. Most kezdek rájönni, hogy mit szabad csinálni és mit nem ahhoz, hogy folyamatos legyen az animáció.
Adódott egy olyan problémám, hogy elindul egy időigényesebb process a háttérben, mikor a user rákattint egy gombra, miközben megy az animáció. Emiatt ennek a szálnak a prioritását levettem kicsire, hogy folyamatos legyen az animáció. Ugyanakkor, ha a user rákattint egy másik gombra, akkor ennek a szálnak minél hamarabb be kellene fejeződnie. Lehetőség van utólag DispatchQueue qos prioritását állítani?
Vagy milyen módszerek vannak az ilyen feladatokra?
Az egyetlen egy ami eszembe jut az az, hogy a háttér feladatot több kisebb feladatra osztom. Ezeket sorban indítom el egymás után. És ha a user megnyomja a gombot, akkor az új feladatok nagyobb prioritással indulnának el. Van még valamilyen más lehetőség?

Lécci segítsetek!

Most először próbálkozom az On Demand Resources lehetőséggel.
Projektben természetesen bejelöltem, hogy használni szeretném a Build Settings-ben.
Valamint azt is beállítottam, hogy a Debug alatt másolja be a main bundlebe, hogy tudjam tesztelni anélkül, hogy feltölteném a Test Fligthra. (EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE)
Megjelöltem kb 50mp3-mat egy taggel.

Be is tölti a tag alapján a könyvtárat, vissza is tér a beginAccessingResources éa persze atz első után conditionallyBeginAccessingResources, hogy betölötte a fájlokat.
Simulátorban meg is tudom nézni, hogy ott van, ahol lennie kell, mindegyiknek.
Megtaláltam a hozzá tartozó plistet, az is helyesnek látszik.
És miután ellenőrzöm, hogy betöltötte-e a fájlokat tag alapján, utána a
Bundle.main.url(forResource: fajl, withExtension: “mp3”) nem talalja.
És van, hogy random 1-1 fájlt megtalál, de a többit nem, pedig mind létezik.

Van valami ötletetek? Mert így hogyan töltssem fel a test Flightba, ha nem is látom, hogy működik-e.

Úgy néz ki már tárgytalan.
Írta a dokumentáció, hogy hozzáférés előtt le kell kérdezni, hogy le van-e töltve, csak azt nem olvastam sehol sem, hogy a hozzáférés előtt közvetlenül kell lekérni. És így már azt is értem, hogy miért volt az, hogy néha működött. Azt hittem csak arra kell odafigyelni, hogy legyen letöltve és teljesen mindegy mikor férünk hozzá. És ha esetleg nincs letöltve, akkor meg csak hibát dob. Mindegy, jó kis tanuló lecke volt ez. :)

Megint lenne egy kérdésem.
Van egy Set típusú tömböm, ami struct típusú elemeket tartalmaz. És szeretném módosítani az egyik elem egyik értékét ( ahol egyezik a keresés eredménye).
Hogy lehet ezt egyszerűsíteni? Most úgy oldom meg, hogy megkeresem az indexet, kiveszem (és eltávolítom) az elemet, módosítom az értéket, majd visszarakom.
A subscript sajnos azt mondja, hogy read-only, így nem engedi közvetlen módosítani.
Szóval hogy lehet ezt egyszerűsíteni?

struct RequestItem {
	var content: String
	var identifier: String
}

var requests = Set<RequestItem>()
...
if let index = self.requests.firstIndex(where: { $0.content == anContent }) {
	var item = self.requests[index]
	item.identifier = anIdentifier
	self.requests.remove(at: index)
	self.requests.insert(item)
}