SwiftとObjective-Cの対称的な違い

画像







この記事では、SwiftとObjective-CがiOS開発者に提供する機能の違いについて説明します。 もちろん、Appleの新しい言語に興味のある開発者はすでに多くの同様の記事を見てきました。そのため、開発プロセスとアプリケーションのアーキテクチャに実際に影響する違いに注目することにしました。 つまり、言語を可能な限り効率的に使用するために知っておくべき違いです。 これらの基準を満たす最も完全なリストを作成しようとしました。







さらに、Swiftが開発にもたらした新機能について話しましたが、Objective-Cと比較して失われたものについて言及することを忘れないようにしました。







各項目について、詳細に入らずに違いの本質を要約し、コード例を示して、逆に詳細にしようとしました。 それらの中で、考慮された違いに直接関係しないものも含め、すべてのニュアンスについてコメントしました。







執筆時点で、Swiftの現在のバージョンは3.0.1です。









1.クラス、構造、および列挙



Swiftのクラスには、Objective-CのNSObjectのような共通の祖先はありません。 さらに、クラスには祖先がまったくない場合があります。







Swiftの構造は、クラスとほぼ同じくらい機能的です。 クラスと同様に、静的および通常のプロパティとメソッド、初期化子、添え字、拡張を持ち、プロトコルを実装できます。 これらは、値によって渡され、継承がないという点でクラスとは異なります。







//  ,   . /*    .  ,      struct  class,    . */ struct Rocket { //      Stage. //   var  ,  let - . var stages: [Stage] /*    Int.     . */ static let maxAllowedStages = 4 /* ,        .     launch() -> Void  launch() -> ()  Void    typealias  ()  ()   .          . */ func launch() { // ... } //    ([Stage]) -> Double static func calculateHeight(for stages: [Stage]) -> Double { /*  reduce     Swift.      .        ,      .  ,        ,    . */ return stages.reduce(0) { (accumulator, stage) -> Double in return accumulator + stage.height } } /* Failable ,   ,      nil.         init. */ init?(stages: [Stage]) { if stages.count > Rocket.maxAllowedStages { return nil } self.stages = stages } /*  (subscript)             . rocket[1] = stage */ /*       ,     . */ /*      ,      . */ subscript (index: Int) -> Stage { get { return stages[index] } set(newValue) { stages[index] = newValue } } } /*  ,        :        . */ protocol TransportableByTrain { func placeOnTrain() -> [Carriage] init(train: [Carriage]) } /*      ,    , , , ,     . */ extension Rocket: TransportableByTrain { func placeOnTrain() -> [Carriage] { return stages.map { stage in return Carriage(content: stage) } } init(train: [Carriage]){ let stages = train.map { $0.content as! Stage } self.init(stages: stages)! } }
      
      





Swiftの列挙には値がない場合があります。







 //   rawValue. enum LaunchState { case preparing, ready, launching, failed, succeeded }
      
      





ただし、値がある場合は、整数だけでなく、実数、文字列、記号も使用できます。 列挙インスタンスは内部値型に自動的にrawValue



れないため、 rawValue



プロパティを使用してそれらにアクセスする必要があります。







 //   rawValue. enum LaunchEvent: Int { case poweredOn = 1, fuelLoaded, oxidizerLoaded, countAutoSequenceStarted, goForlaunchVerification, preLaunchChecks, pressurizePropellantTanks, ignitionSequenceStart, liftoff } let lastEvent = LaunchEvent.liftoff lastEvent.rawValue // 9 /*   `rawValue`   failable    . */ let firstEvent = LaunchEvent(rawValue: 1) //LaunchSequence.poweredOn let nonexistentEvent = LaunchEvent(rawValue: 0) //nil
      
      





列挙にrawValue



がない場合、列挙の各ケースに独自の値を関連付けることができます。 いくつかあり得、それらは任意のタイプであり得る。







 //    . enum LaunchError { case compromisedHullIntegrity(stage: Stage) case engineMalfunction(engine: Engine, malfunction: Malfunction) case unrecognizedError }
      
      





列挙と構造は、値によって渡されます。 また、保存されたプロパティを除き、上記と同じ機能を備えています。 列挙プロパティは計算プロパティのみです。







 //     . extension LaunchEvent { static var sequence: [LaunchEvent] { return Array(1...9).map { LaunchEvent(rawValue: $0)! } } }
      
      





転送の詳細: [1]







この構造と列挙の豊富な機能により、オブジェクトよりも値が適切なクラスの代わりにそれらを使用できます。 この分離の目的は、アプリケーションのアーキテクチャを簡素化することです。 複雑さ管理の詳細: [2]







2.関数、メソッド、およびクロージャーのタイプ



Swiftでは、関数、メソッド、およびクロージャーはファーストクラスの市民です。つまり、それらは型であり、変数に格納され、パラメーターとして関数に渡されます。 関数、メソッド、およびクロージャーのタイプは、返される値と受け入れられる値によってのみ決定されます。 つまり、特定の型の変数が宣言されている場合、関数とメソッドまたはクロージャーの両方をその中に保存できます。 これらのタイプのインスタンスは参照渡しされます。







このようなエンティティの統合により、エンティティの使用が簡素化されました。 Objective-Cでは、オブジェクトとセレクターを渡すか、ブロックを渡すと、原則として同じ問題が解決されました。 Swiftでは、同様のAPIには、特定の受け入れられた値と返された値、およびそこに正確に転送されるもの(関数、メソッド、またはクロージャー)が必要です。 関係ありません。







 /*  reduce,     calculateHeight   . (Result, (Result, Element) throws -> Result) rethrows -> Result       ,    : throw  rethrows;  generic  Result    Element     Double  Stage.     ,   reduce   . (Double, (Double, Stage) -> Double) -> Double    :   Double,   - ,  Double  Stage,   Double.   ,   ,   Double. */ //          : let totalHeight = stages.reduce(0, { (accumulator: Double, stage: Stage) -> Double in return accumulator + stage.height }) /*      . -,    reduce        -,     . -,   -    ,      . */ let totalHeight = stages.reduce(0) { accumulator, stage in return accumulator + stage.height } /*    .       .        .         $0, $1     .      ,      return. */ let totalHeight = stages.reduce(0) { $0 + $1.height } /*  ,   swift     ,   ,   .     +     Double  Stage,        [Stage]     [Double],       reduce,       (Double, Double) -> Double.   Double  + ,          . */ let totalHeight = stages.map{ $0.height }.reduce(0, +)
      
      





3.デフォルト設定



関数とメソッドのパラメーターにはデフォルト値を設定できます。

いくつかの関数/メソッドの代わりにデフォルトのパラメーターを使用すると、コードの量が減り、コードが減り、バグが減ります。







 enum Destination { case lowEarthOrbit, geostationaryEarthOrbit, transLunarInjection } class RocketFactory { /*  -         '='. */ func makeRocket(destination: Destination = Destination.lowEarthOrbit, payloadMass: Double = 6450) -> Rocket { //... } } let rocketFactory = RocketFactory() //    -    . let soyuz = rocketFactory.makeRocket() let protonM = rocketFactory.makeRocket(destination: Destination.geostationaryEarthOrbit) let saturnV = rocketFactory.makeRocket(destination: Destination.transLunarInjection, payloadMass: 48600)
      
      





4.オプション



どのタイプの変数もnil



値を取ることはできません。 Swiftは特別なOptional



型を使用します。値の欠如を想像する必要がある場合、他の型は「ラップ」します。







Optional



は、 none



some



2つの場合の列挙です。 Optional.some(Wrapped)



には、関連付けられた値としてラップされた型の値が含まれます。

Optional.none



、リテラルnil



Optional.none



同等です。

[ Optional



、参照型または値渡しとしてラップできます。







 struct Launchpad { //    optional . var rocket: Rocket? /*        : var rocket: Optional<Rocket> */ }
      
      





オプション値のプロパティとメソッドにアクセスするには、まずこれらのオプション値を展開する必要があります。つまり、それらがnil



ないことを確認します。 もちろん、これは、たとえばswitchを使用するなど、通常の列挙のようにオプションの値を操作することで実現できますが、Swiftにはこれに便利な構造があります。if if let



guard let else



; 演算子?



!



??









 /*  ,     optional  -   '!'.    optional    ,   runtime error   ,   nil. */ launchpad.rocket!.launch() func start() { /*    optional     if let. */ if let rocket = launchpad.rocket { /*  ,         optional . */ rocket.launch() } else { //    else      optional . abortStart() } } /*  ,      ,  optional   nil,   optional     guard let else. */ func start2() { /*    if let       optional    ,   else      nil    . */ guard let rocket = launchpad.rocket else { abortStart() return } rocket.launch() } /*        optional ,   .   '?'.              .       optional. */ launchpad.rocket?.launch() var possibleLaunchpad: Launchpad? //      optional . possibleLaunchpad?.rocket?.launch() possibleLaunchpad?.rocket?.stages //Return type: [Stages]? /*     optional    '??'.    .  - optional ,   -  optional   .     nil,     ,    . */ let certainRocket = possibleLaunchpad?.rocket ?? rocketFactory.makeRocket()
      
      





このような制限により、nil値を予期せずヒットさせることが難しくなり、Swiftコードの信頼性が高まります。







5.ネストされたタイプ



Swiftでは、ネストされた型を宣言できます。つまり、クラス、構造、および列挙を相互に宣言できます。







 /*         ,     - . */ struct Launch { enum State { case preparing, ready, launching, failed, succeeded } //          . var state: State = .preparing } /*    ,   ,      . */ let launchState: Launch.State
      
      





関数は他の関数内で宣言することもできます。 しかし、実際には、内部関数はクロージャーであり、外部関数のコンテキストをキャプチャできます。







6.タプル



Swiftの新しいタイプはタプルです。 タプルを使用すると、任意のタイプの複数の値を1つの複合値に結合できます。 タプルは値で渡されます。







 /*   ,    ,      ,    . */ var launchEventMark: (event: LaunchEvent, timeMark: Int) = (.ignitionSequenceStart, 6600) launchEventMark.event launchEventMark.timeMark // ,        . launchEventMark.0 launchEventMark.1 /*  ,          ,   ,             . */ var anotherMark: (LaunchEvent, Int) = launchEventMark anotherMark.0 anotherMark.event // error: type has no member 'event'
      
      





7.ゲッター、セッター、プロパティオブザーバー



Objective-Cとは異なり、Swiftでは、ゲッターとセッターは計算されたプロパティに対してのみ定義できます。 もちろん、getterやsetterなどの保存されたプロパティには、メソッドまたは計算されたプロパティを使用できます。







 //  getter'  setter'    . class ThrustController { init(minThrust: Double, maxThrust: Double, currentThrust: Double) { self.minThrust = minThrust self.maxThrust = maxThrust thrust = currentThrust } var minThrust: Double var maxThrust: Double private var _thrust = 0.0 var thrust: Double { get { return _thrust } set { if newValue > maxThrust { _thrust = maxThrust } else if newValue < minThrust { // _thrust = maxThrust } else { _thrust = newValue } } /* -,    setter'    newValue,       : */ // set(thrustInput) { ... } } } /* , ,      ,         ,     . */
      
      





プロパティの値の変化を追跡する必要があるソリューションのタスクについては、新しいメカニズム-プロパティオブザーバーが登場しました。 格納されているプロパティに対して定義できます。 これらには、 willSet



(プロパティ値を変更する前に呼び出される)とdidSet



(新しい値を設定した直後に呼び出される)の2つのタイプがあります。







 protocol ThrustObserver: class { func thrustWillChange(from oldValue: Double, to newValue: Double) func thrustDidChange(from oldValue: Double, to newValue: Double) } class ThrustMeter { weak var observer: ThrustObserver? var thrust: Double = 0.0 { willSet { observer?.thrustWillChange(from: thrust, to: newValue) } didSet { observer?.thrustDidChange(from: oldValue, to: thrust) } //      set,  newValue  oldValue    . // willSet(newThrust) { ... } // didSet(oldThrust) { ... } } }
      
      





プロパティオブザーバ構造の場合、宣言されているプロパティの値を直接変更するだけでなく、任意の深さのネストされたプロパティを変更するためにも呼び出されることに注意してください。







Objective-Cではgetterを介して実装できる遅延初期化のために、Swiftにはlazy



プロパティ修飾子があります。







8.プロパティとコレクションの可変性



Swiftでは、型プロパティは定数にすることができます。 さらに、定数として宣言されたプロパティの型がクラス、つまり参照によって渡される型である場合、リンク自体のみが不変になります。 つまり、このプロパティに新しいオブジェクトを割り当てることはできませんが、このオブジェクトのプロパティを変更できます。 値で渡される型の場合、変更は無効になります。







 class ThrustMeterClass { var thrust: Double init(thrust: Double) { self.thrust = thrust } } struct ThrustMeterStruct { var thrust = 0.0 } let thrustMeterClass = ThrustMeterClass(thrust: 0) thrustMeterClass = ThrustMeterClass(thrust: 50) //Error thrustMeterClass.thrust = 50 //OK let thrustMeterStruct = ThrustMeterStruct(thrust: 0) thrustMeterStruct = ThrustMeterStruct(thrust: 50) //Error thrustMeterStruct.thrust = 50 //Error
      
      





Swiftではすべてのコレクションは構造体であるため、Objective-Cのようにタイプによってではなく、宣言の方法(定数または変数)によってその可変性が決定されます。 標準ライブラリには、配列、多数、および辞書の3つのコレクションがあります。







 let immutableArray = [1, 2, 3] var mutableArray = [1, 2, 3]
      
      





9.タイプが関連付けられたプロトコル



従来のプロトコルは、Objective-Cの対応するプロトコルと実質的に違いはありませんが、関連するタイプのプロトコルは、Swiftのまったく新しい設計です。 プロトコルは、関連する型を宣言し、メソッドおよびプロパティの要件のプレースホルダーとして使用できます。 そして、このプロトコルを実装するものは、どの実際のタイプが使用されるかをすでに示しているはずです。







 //        . protocol Fuel {} protocol Oxidizer {} //   ,   . struct Hydrazine: Fuel {} struct ChlorineTrifluoride: Oxidizer {} struct Kerosene: Fuel {} struct Oxygen: Oxidizer {} //    . protocol Bipropellant { /*           . */ associatedtype TFuel: Fuel associatedtype TOxidizer: Oxidizer func burn(_ fuel: TFuel, with oxidizer: TOxidizer) } //      . struct KoxPropellant: Bipropellant { /*  ,          ,    typealias.                  burn. */ typealias TOxidizer = Oxygen typealias TFuel = Kerosene func burn(_ fuel: Kerosene, with oxidizer: Oxygen) { print("Burn of kerosene with oxygen.") } } /*             Objective-C. */ protocol Hypergolic: Bipropellant {} struct HctPropellant: Bipropellant, Hypergolic { typealias TOxidizer = ChlorineTrifluoride typealias TFuel = Hydrazine func burn(_ fuel: Hydrazine, with oxidizer: ChlorineTrifluoride) { print("Burn of hydrazine with chlorine trifluoride.") } }
      
      





通常のプロトコルは特定のタイプとして使用できますが、







 struct AnyFuelTank { /*        ,   Fuel. */ var content: Fuel } var fuelTank = AnyFuelTank(content: kerosene) fuelTank.content = hydrazine
      
      





タイプが関連付けられたプロトコルは、このようには使用できません。







 struct RocketEngine { //    . var propellant: Bipropellant /* Error: Protocol 'Bipropellant' can only be used as a generic constraint because it has Self or associated type requirements */ }
      
      





エラーメッセージからわかるように、関連付けられた値を持つプロトコルは、ジェネリック型の制限としてのみ使用できます。







 struct RocketEngine<TBipropellant: Bipropellant> { var propellant: TBipropellant }
      
      





これがなぜであり、どのように共存するかについてのいくつかの考えは、ここで見つけることができます: [3]







一般に、関連する値を持つプロトコルは、次の一連の記事で詳しく説明されています: [4]







10.プロトコル拡張



Swiftのクラス、構造、および列挙の拡張(拡張)は、Objective-Cのカテゴリと拡張に基本的に似ています。つまり、ソースコードにアクセスできない場合でも、型に動作を追加できます。 型拡張を使用すると、計算されたプロパティ、メソッド、初期化子、添え字、ネストされた型を追加し、プロトコルを実装できます。







プロトコル拡張は、Swiftの新機能です。 これらにより、このプロトコルを実装する型にプロパティとメソッドのデフォルト実装を提供できます。 つまり、プロトコル拡張は要件を記述するのではなく、このプロトコルを実装するタイプが受け取る特定の実装を記述します。 もちろん、型はこの実装をオーバーライドできます。 このようなデフォルトの実装により、Objective-Cとの互換性の一部としてのみSwiftに存在するオプションのプロトコル要件を置き換えることができます。







 //  ,   ,    . extension Bipropellant { func burn(_ fuel: TFuel, with oxidizer: TOxidizer) { print("Common burn.") } }
      
      





さらに、プロトコル拡張では、プロトコル要件にないメソッドを実装できます。また、プロトコルを実装するこれらのメソッドはそれらを受信することもできます。 ただし、静的バインディングのため、プロトコル拡張はこの方法で注意して使用する必要があります。 詳細はこちら: [5]







さらに、プロトコルを実装するすべてのタイプがデフォルトの実装を受信しないように、プロトコル拡張を指定することもできます。 条件によっては、型が特定のクラスを継承するか、特定のプロトコルを実装することが必要になる場合があります。 プロトコルを実装する型自体および関連する型に条件を課すことができます。 異なる拡張機能が同じメソッドの実装を提供し、その型がいくつかの拡張機能の条件を満たしている場合、その拡張機能の条件がより具体的である実装を受け取ります。 そうでない場合、型は実装を受け取りません。







 /*      ,  ,        . */ extension Bipropellant where Self.TOxidizer == Oxygen { func burn(_ fuel: TFuel, with oxidizer: TOxidizer) { print("Burn with oxygen as oxidizer.") } } /*      .    . */ extension Bipropellant where Self: Hypergolic, Self.TFuel == Hydrazine { func burn(_ fuel: TFuel, with oxidizer: TOxidizer) { print("Self-ignited burn of hydrazine.") } }
      
      





 /*      KoxPropellant  HctPropellant     burn,         Bipropellant. */ let koxPropellant = KoxPropellant() koxPropellant.burn(kerosene, with: oxygen) // Burn of kerosene with oxygen. let hctPropelant = HctPropellant() hctPropelant.burn(hydrazine, with: chlorineTrifluoride) // Burn of hydrazine with chlorine trifluoride. //        burn,      . koxPropellant.burn(kerosene, with: oxygen) // Burn with oxygen as oxidizer. hctPropelant.burn(hydrazine, with: chlorineTrifluoride) // Self-ignited burn of hydrazine.
      
      





WWDC15 “Protocol-Oriented Programming in Swift” by Dave Abrahams [6]







11. Generics



Objective-C, Swift generic , , .







Swift generic , Objective-C, . , , .







 //   Tank      . struct Tank<TContent> { var content: TContent } //    . struct FuelTank<TFuel: Fuel> { var content: TFuel } //       . struct OxygenEngine<TBipropellant: Bipropellant> where TBipropellant.TOxidizer == Oxygen { var fuel: TBipropellant.TFuel var oxidizer: Oxygen }
      
      





12.



Objective-C .







Swift , . , , .







13.



Objective-C . , — .







Swift , .







スウィフト3:









14.



Objective-C : NSException



NSError



. NSException



@try



, @catch



, @finally



; NSError



NSError*



. Cocoa NSException



, NSException



, , , NSError



.







Swift do-try-catch



, NSError



. finally



. defer



, scope, , , .







NSException



, , - Objective-C, , Swift .







 protocol Technology {} /*       ,    Error.     ,       throw  catch. */ //     . enum ConstructingError: Error { case notEnoughFunding(shortage: Double) case neccessaryTehcnologyIsNotAvailable(technology: Technology) case impossibleWithModernTechnology } class ThrowingRocketFactory { var funds: Double = 0.0 func estimateCosts() -> Double { return 0.0 } /* ,    ,      throws  . */ func makeRocket(for destination: Destination, withPayloadMass payloadMass: Double) throws -> Rocket { //... if funds <= 0 { throw ConstructingError.notEnoughFunding(shortage: estimateCosts()) } //... } //... } let factory = ThrowingRocketFactory() let destination = Destination.lowEarthOrbit let payloadMass = 0.0 //          try. //   ,         . /* 1)    .   ,     ,    throws. */ func getRocket(forDestination destination: Destination, payloadMass: Double) throws -> Rocket { let rocketFactory = ThrowingRocketFactory() let rocket = try rocketFactory.makeRocket(for: destination, withPayloadMass: payloadMass) return rocket } /* 2)   .          do,    catch     . */ do { let rocket = try factory.makeRocket(for: destination, withPayloadMass: payloadMass) } catch ConstructingError.notEnoughFunding(let shortage) { print("Find money: \(shortage)") } catch ConstructingError.neccessaryTehcnologyIsNotAvailable(let technology) { print("Find alternatives for: \(technology)") } catch { print("Impossible to create such rocket.") } /* 3)       optional,     . */ if let rocket = try? factory.makeRocket(for: destination, withPayloadMass: payloadMass) { //... } /* 4)    .      ,        runtime. */ let rocket = try! factory.makeRocket(for: destination, withPayloadMass: payloadMass)
      
      





 /*    defer ,          ,       . */ extension ThrowingRocketFactory { func openCommunications() {/* ... */} func closeCommunications() {/* ... */} } do { factory.openCommunications() defer { /*    defer       scope,   ,  do.        -        . */ factory.closeCommunications() } let rocket = try factory.makeRocket(for: destination, withPayloadMass: payloadMass) } catch { // ... }
      
      





15.



Swift, Objective-C, , . API Unmanaged



. : retain()



, release()



, autorelease()



; , , Swift'. : takeRetainedValue()



— , takeUnretainedValue()



— .







Unmanaged



: [7]







16.



Swift . atomic



nonatomic



Objective-C. , , Grand Central Dispatch NSOperation



Cocoa.







17.



Objective-C Swift . Swift (build configurations). ( -D <#flag#>



) , — os()



: OSX



, iOS



, watchOS



, tvOS



, Linux



; — arch()



: x86_64



, arm



, arm64



, i386



; — swift()



: >= . : #if



, #elseif



, #else



, #endif



.

Apple docs.







18.



Objective-C Swift ? , runtime Swift . , OCMock Swift , NSObject



.







Swift Objective-C ? , Objective-C. Swift Objective-C, NSObject



. Swift .

Apple docs.







Objective-C



Objective-C [8] :







  1. runtime. , , .
  2. - , . , , , , .
  3. . , runtime .


, Cocoa . Swift , iOS, Cocoa , .







Swift open source , Apple , Cocoa. . , , , Cocoa , , - Apple Cocoa - , Swift. , Objective-C ? , , , Swift + Cocoa, Swift.







19. “Target/action” messaging



“Target/action” messaging . responder chain UI Interface Builder 1988 , .







Swift + Cocoa. responder UIResponder



UIKit, , , .







Swift . responder chain runtime . [9]







20. Key-Value Coding



, .







Swift + Cocoa. Swift KVC , NSObject



.







Swift?

read-only, (reflection) Mirror



. [10]







21. Key-Value Observing



“” - .







Swift + Cocoa. KVO , NSObject



, dynamic



, .







Swift , , , , , property observers. : , Observable-Swift , , Cocoa KVO .







22. NotificationCenter



, .







Swift + Cocoa. API . ( ) , Objective-C ( NSObject



@objc



). , API , - NSObjectProtocol



, . Cocoa .







Swift. “” , Swift . , NS , , NotificationCenter



Swift Foundation, Cocoa .







23. UndoManager



, .







Swift + Cocoa. 3 : , NSInvocation



. . : , NotificationCenter



: Objective-C. NSInvocation



, , , , NSObject



. iOS 9.







Swift. UndoManager



, , NotificationCente



r, Swift, UndoManager



Swift Foundation.









, API , , , Swift API, .







, , Objective-C, Swift . Swift . , — -. , , Objective-C KVC, CoreData, bindings, HOM, UndoManager .. /. , Swift , . [11]







Swift



Swift? ?







1. . Swift , Objective-C. , . . : [12] [13] .







2. ? , , Swift , Objective-C. , , iOS , Cocoa.







, . Swift Bool



, .







おわりに



Swift Objective-C .







-, source stability. , 3.0, , , Swift , . , . , Availability API. [14]







-, Swift ABI . , .







, , Swift , , .







Swift 3 ABI Swift 4, 2017 . [15]







, , Swift Objective-C, , , , .









参照資料



1. Advanced & Practical Enum usage in Swift

2. Controlling Complexity in Swift — or — Making Friends with Value Types

3. Protocols with Associated Types

4. Swift: Associated Types

5. The Ghost of Swift Bugs Future

6. Protocol-Oriented Programming in Swift

7. Unmanaged

8. A Definition of Dynamic Programming in the Cocoa World

9. Pimp My Code, Book 2: Swift and Dynamism

10. The Swift Reflection API and what you can do with it

11. What's Missing in the Discussion about Dynamic Swift

12. The Type System is Your Friend

13. Enums as constants

14. Availability by Swift version

15. Looking back on Swift 3 and ahead to Swift 4








All Articles