Swift3.0-preview-1で追加されたswift packageを試してみた

Swift3.0-preview-1で追加されたswift packageを試してみた

f:id:moapp:20160606013623p:plain

github.com

3.0-preview-1-SNAPSHOT-2016-05-31-aのSwiftがリリースされ、このバージョンからswift packageコマンドが使えるようになりました。

これはSwift Package Managerを使用する際に利用するコマンドになります。この記事では環境構築、使い方を説明していきます。

Swift Package Managerの環境を構築する

SwiftのバージョンをSwift3.0-preview-1版に変更する

swift packageコマンドが使えるようになった3.0-preview-1-SNAPSHOT-2016-05-31-aのSwiftを使用します。

swift.orgからSwift3.0-preview-1版のsnapshotをダウンロードしてくる

swift.org

上記からSwift3.0-preview-1版Swiftのsnapshotをダウンロードし、インストールしておきます。

swiftenvでSwiftのバージョンをSwift3.0-preview-1版に切り替える

moapp.hateblo.jp

今回はswiftenvを利用してSwiftのバージョンを変更します。

swiftenvは上記記事にてセットアップについて説明してますので参考にしてください。

swiftenv global 3.0-preview-1-SNAPSHOT-2016-05-31-a

これで設定してるSwiftが3.0-preview-1-SNAPSHOT-2016-05-31-aのバージョンに変更されました。

念のためにバージョンが変更されたか確認しておきましょう。

$ swift --version
Apple Swift version 3.0-dev (LLVM 3863c393d9, Clang d03752fe45, Swift e996f0c248)
Target: x86_64-apple-macosx10.9
$ swiftenv version
3.0-preview-1-SNAPSHOT-2016-05-31-a (set by /Users/__moai/.swiftenv/version)

ここでバージョンが切り替わっていれば成功です。

TOOLCHAINSにパスを通す

swift packageコマンドを利用するためにパスを通します。

OS、環境によって違うので、それに合わせて変更しましょう。

Xcode 7.2の場合

export PATH=/Library/Toolchains/swift-latest.xctoolchain/usr/bin:$PATH

Xcode 7.3の場合

export TOOLCHAINS=swift

Linuxの場合

export PATH=path/to/toolchain/usr/bin:$PATH

swift packageがの環境が構築できたか確認する

swift packageのコマンドが利用できるようになったかを確認します。

バージョンが表示されるかどうか以下のコマンドを叩いて確認しましょう。

$ swift package --version
Swift Package Manager – Swift 3.0

エラーが出る場合はSwiftのバージョン、pathの設定がおかしい可能性があるのでその辺りを調べてみると良いかもしれません。

Swift Package Managerを試してみる

それではswift packageコマンドを試していきましょう。

swift packageコマンドを叩いてみます。

$ swift package
OVERVIEW: Perform operations on Swift packages

USAGE: swift package [command] [options]

COMMANDS:
  init [--type <type>]                   Initialize package (library|executable)
  fetch                                  Fetch package dependencies
  update                                 Update package dependencies
  generate-xcodeproj [--output <path>]   Generates an Xcode project
  show-dependencies [--format <format>]  Print dependency graph (text|dot|json)
  dump-package [--output <path>]         Print Package.swift as JSON

OPTIONS:
  -C, --chdir <path>        Change working directory before any other operation
  --color <mode>            Specify color mode (auto|always|never)
  -v, --verbose             Increase verbosity of informational output
  --version                 Print the Swift Package Manager version
  -Xcc <flag>               Pass flag through to all C compiler invocations
  -Xlinker <flag>           Pass flag through to all linker invocations
  -Xswiftc <flag>           Pass flag through to all Swift compiler invocations

NOTE: Use `swift build` to build packages, and `swift test` to test packages

上記のように使い方が出力されてますね。

swift packageから雛形を作る

Hogeディレクトリを作成して0の状態からPckageの雛形を作成します。

雛形を作成するときにはswift package initコマンドを叩きます。

mkdir Hoge
cd Hoge
swift package init

すると、下記のログが出力され雛形が生成されます。

Creating library package: Hoge
Creating Package.swift
Creating .gitignore
Creating Sources/
Creating Sources/Hoge.swift
Creating Tests/
Creating Tests/LinuxMain.swift
Creating Tests/Hoge/
Creating Tests/Hoge/HogeTests.swift

それでは自動生成されたファイルの中身を見ていきましょう。

Package.swift

ディレクトリ名をそのままPackage名として定義しています。

import PackageDescription

let package = Package(
    name: "Hoge"
)

.gitignore

ビルドによる成果物、依存してるPackage群、Xcodeプロジェクトファイルなどがignoreとしたいファイルのようです。

.DS_Store
/.build
/Packages
/*.xcodeproj

Sources/Hoge.swift

ディレクトリ名の構造体が定義されており、textのプロパティにはHello, World!が値として入っています。

雛形的にrootの構造体としてここからライブラリにアクセスさせたい意図があるようにも思えます。

struct Hoge {

    var text = "Hello, World!"
}

Tests/LinuxMain.swift

Tests/LinuxMain.swiftがテストの際に最初に呼ばれるファイルになります。

ここではターゲットとなるTestの全クラスをXCTMainに渡して教えてあげています。

import XCTest
@testable import HogeTestSuite

XCTMain([
     testCase(HogeTests.allTests),
])

Tests/Hoge/HogeTests.swift

自動生成するHoge構造体が保持してるtextの中身をテストしています。

先ほどのTests/LinuxMain.swiftで見たようにallTestsとしてTestする内容の一覧を返すようにしています。

import XCTest
@testable import Hoge

class HogeTests: XCTestCase {
    func testExample() {
        // This is an example of a functional test case.
        // Use XCTAssert and related functions to verify your tests produce the correct results.
        XCTAssertEqual(Hoge().text, "Hello, World!")
    }


    static var allTests : [(String, (HogeTests) -> () throws -> Void)] {
        return [
            ("testExample", testExample),
        ]
    }
}

swift packageからXcodeのプロジェクトファイルを生成する

Hogeディレクトリで自動生成したファイルを元にXcodeのプロジェクトファイルを生成します。

下記コマンドを先ほどのHogeディレクトリ上で叩きます。

swift package generate-xcodeproj

すると下記ログが出力されプロジェクトファイルが生成されます。

generated: ./Hoge.xcodeproj

早速Xcodeで開いてみましょう。

f:id:moapp:20160606012850p:plain

自動でコードがプロジェクトファイルに紐付けされた状態になっていますね。

このようにswift packageコマンドを使えばSwiftのコーディングに集中することができます。

どんどんSwiftで良いコードを書いていきましょう。

swiftenvでSwiftのバージョンを管理する

swiftenvでSwiftのバージョンを管理する

f:id:moapp:20160602170720p:plain

Swift3.0の開発版が公開されてからSwiftの2系と3系の間のタイミングである今、iOSなどのアプリ開発ではSwiftの2系、Swift3の変更点の確認ではSwift3.0というような使い分けをする場面が出てくるかもしれません。

そのような時こそSwiftのバージョン管理をしてくれるswiftenvがオススメです。

Homebrewからswiftenvをインストールする

Macで簡単に設定をするためにHomebrewからインストールします、ターミナル上から下記コマンドを入力します。

brew install kylef/formulae/swiftenv

swiftenvのpathを設定する

インストールが完了したらswiftenvのパスを設定します、使ってるシェル環境に合わせて設定しましょう。

bash

~/.bash_profileの部分は環境によって変わると思うので、環境に合わせて~/.bashrcなり変更してください

echo 'if which swiftenv > /dev/null; then eval "$(swiftenv init -)"; fi' >> ~/.bash_profile

zsh

echo 'if which swiftenv > /dev/null; then eval "$(swiftenv init -)"; fi' >> ~/.zshrc

fish

echo 'status --is-interactive; and . (swiftenv init -|psub)' >> ~/.config/fish/config.fish

swiftenvを反映する

swiftenv側で設定してるSwiftを参照するように反映させます。

swiftenv rehash

swiftenvを動かしてみる

swiftenvコマンドを入力してヘルプが出れば設定が正常に完了になります、基本的にはpyenvrbenvと使い方は変わりません。

$ swiftenv 
Usage: swiftenv [--version] <command>

  version   Displays the current active Swift version
  versions  Lists all installed Swift versions
  global    Sets the global version of Swift
  local     Sets the local application-specific version of Swift
  install   Installs a version of Swift
  uninstall Uninstalls a specific Swift version
  rehash    Installs shims for Swift binaries

Visit https://swiftenv.fuller.li for more info.

現在の環境で設定されてるSwiftのバージョンを確認する

swiftenvで管理してるSwiftのバージョンのうち、設定してるバージョンを確認します。

systemというのがswiftenvで管理する前からインストールされているSwiftになります。

$ swiftenv version
system (set by /Users/__moai/.swiftenv/version)

swiftenvで管理してる複数のSwiftのバージョンを確認する

swiftenvで管理してるSwiftのバージョンの一覧を確認します。

下記のようにsnapshotのタグ名で管理されていることが分かります。

$ swiftenv versions
* system
  3.0-preview-1-SNAPSHOT-2016-05-31-a
  DEVELOPMENT-SNAPSHOT-2016-05-09-a
  2.2

設定するSwiftのバージョンを変更する

swiftenvでSwiftのバージョンを切り替える際は、localglobalという概念の2種類があります。

localがそのディレクトリ内だけでSwiftのバージョンを変えたい場合、globalが全てのディレクトリでSwiftのバージョンを変えたい場合に使い分けます。

まずはglobalでSwiftのバージョンを変更してみます。

swiftenv global 3.0-preview-1-SNAPSHOT-2016-05-31-a

Swiftのバージョンが変わったどうか確認します。

$ swift --version
Apple Swift version 3.0-dev (LLVM 3863c393d9, Clang d03752fe45, Swift e996f0c248)
Target: x86_64-apple-macosx10.9
$ swiftenv version
3.0-preview-1-SNAPSHOT-2016-05-31-a (set by /Users/__moai/.swift-version)

このようにSwiftから見ても、swiftenvから見てもバージョンを変更したことが確認できます。

それでは続いてlocalでSwiftのバージョンを変更してみます。

ここではhogeディレクトリを新しく作成し、その中でSwiftのバージョンをsystemに変更します。

mkdir hoge
cd hoge
swiftenv local system

ここで変わったことを確認します。

$ swift --version
Apple Swift version 2.2 (swiftlang-703.0.18.8 clang-703.0.31)
Target: x86_64-apple-macosx10.9
$ swiftenv version
system (set by /Users/__moai/hoge/.swift-version)

Swiftのバージョンが先ほどと同じswiftenvを入れる前のSwiftのバージョンになっていることが確認できました。

仕組みとしてはlocalで切り替えたSwiftのバージョンは、ディレクトリ内に.swift-versionというファイルが作成されており、その中でSwiftのバージョンが記載されています。

なのでhogeディレクトリ内のSwiftのバージョンを再度globalのSwiftのバージョンに変更したい場合はこの.swift-versionを削除すれば反映されます。

rm .swift-version

おわりに

このようにSwiftのバージョン管理を楽にしてくれる便利なswiftenvの紹介でした。

みなさんもどんどんバージョンアップが進むSwiftの変更を追いかける際の便利な道具として是非試してみてください。

Swift3.0で文字列操作

Swift3.0で文字列操作

f:id:moapp:20160601182612p:plain

Swiftでは文字列周りの操作はFoundationのライブラリを使うことで簡単に操作することができます。

ですが今回はあえてFoundationを使わないで文字列の操作を一通り行うコードを乗せつつ説明していきます。

文字列同士を結合する

文字列の結合は単純に「+演算子を繋げるだけです。

"123" + "456" // -> "123456"

対象の文字列の配列から1つの文字列に集約する

文字列を配列から集約する場合はjoined(separator: String)関数を使用します。

["1", "2", "3", "4", "5", "6"].joined(separator: "") // -> "123456"

対象の文字列から指定の1文字の位置を検索する

対象の文字列をCharacterの配列として扱い、enumeratedで文字とインデックスを取るようにしておきます。

そして指定の1文字でfilterをかけて、最初にヒットしたインデックスの値を元にString.Indexの値として取得します。

この例では取得したString.Indexの値が取れてることを確認するためにsubscriptでアクセスできるかどうかを確認しています。

extension String {
    
    func index(character: Character) -> String.Index? {
        let index = self.characters.enumerated().filter { (idx, c) in c == character }.first?.0
        guard let offset = index else {
            return nil
        }
        return self.index(self.startIndex, offsetBy: offset)
    }
}

let string = "123456"
if let index = string.index(character: "3") {
    string[index] // -> "3"
}

対象の文字列から指定の文字列の範囲を検索する

対象の文字列が指定の文字列の先頭1文字を含んでいた場合に、一致したインデックスから指定の文字列の長さの数だけ終端のインデックスを取得しておきます。

先頭から終端のインデックスを元にRangeを用意して対象の文字列をスライスし指定の文字列と同じかどうかを比較します。

同じだった場合のみRangeを返します。

extension String {

    func range(string: String) -> Range<String.Index>? {
        guard let startIndex = self.index(character: string[string.startIndex]) else {
            return nil
        }
        
        guard let endIndex = self.index(startIndex, offsetBy: string.characters.count, limitedBy: self.endIndex) else {
            return nil
        }
        
        let range = startIndex..<endIndex
        if self[range] != string {
            return nil
        }
        
        return range
    }
}

let string = "123456"
if let range = string.range(string: "34") {
    string[range] // -> "34"
}

指定する1文字ごとに対象の文字列を分割する

対象の文字列をCharacterの配列として扱い、split(separator: String)関数を利用して分割しています。

extension String {

    func separatedComponents(separator: Character) -> [String] {
        return self.characters.split(separator: separator).map(String.init)
    }
}

"1,2,3,4,5,6".separatedComponents(separator: ",") 
// -> ["1", "2", "3", "4", "5", "6"]

指定する1文字を対象の文字列から取り除く

対象の文字列をCharacterの配列として扱い、指定する1文字でfilterをかけてもう一度文字列として集約をかけます。

extension String {
    
    func removed(character: Character) -> String {
        return self.characters.filter { $0 != character }.map { String($0) }.joined(separator: "")
    }
}

"1,2,3,4,5,6".removed(character: ",") // -> "123456"

指定する文字列を対象の文字列から取り除く

replaceSubrange(range: Range<String.Index>, with: String)を内部で再帰的に呼び出して文字列を取り除きます。

extension String {
    
    func removed(string: String) -> String {
        if let range = self.range(string: string) {
            var mutatingSelf = self
            mutatingSelf.replaceSubrange(range, with: "")
            return mutatingSelf.removed(string: string)
        }
        return self
    }
}

"1, 2, 3, 4, 5, 6".removed(string: ", ") // -> "123456"

Xcodeで開発版のSwift3.0を試してみた

XcodeでSwift3.0の開発版を動かす

f:id:moapp:20160531093401p:plain

Swiftが世に出てから2年経ち、オープンソース化されてから半年経とうとしています。

Swift3は今までの下位バージョンとの互換性を意識せず大幅な変更を加えており、ベースとなるProtocol名の一新、C言語のfor文のスタイル撤廃、カリー化の文法変更などがあり、よりモダンな言語として生まれ変わっています。

そしてSwift3以降の変更に関しては最小限の変更で留めてアップデートをしていくとのことで、Swift3から変更点を追いかけてもSwift4以降も十分に追いかけられるでしょう。

そこでMac OSXcodeを使って開発版のSwift3.0を動かしてみます。

Swift3.0を試す開発環境

Mac OSXcodeは現時点での最新で試しています。

Swift3.0のsnapshotをダウンロードする

Swift.orgからXcodeの最新版をダウンロードします。(現時点だとMay 9, 2016

snapshotをインストールする

ダウンロードしてきたpkgファイルをインストールしましょう。

f:id:moapp:20160531085909p:plain

Xcodeにsnapshotを反映させる

準備としてはこれだけであとはXcodeを開いてSwift3.0のsnapshotをXcode側に設定します。

Xcodeを起動して、メニューからXcode > Toolchainsを選択し先ほどインストールしたsnapshotを選択してください。

f:id:moapp:20160531090536p:plain

Swift3.0をXcodeで動かす

それではXcodeで動かすための環境ができたので、新しく変更されたカリー化の文法を試してみます。

func calclate(_ a: Int) -> (Int) -> Int {
    return { b in
        return a + b
    }
}

let calclator = calclate(3)
print(calclator(10))
print(calclator(14))

上記のコードをPlaygroundで動かそうとしたのですがsnapshotからだと動かないのか反応しなくなりました。

なので適当なプロジェクトを用意して、そこで動かします。

f:id:moapp:20160531091717p:plain

ちゃんとカリー化できてることが確認できてますね。

このように新しいバージョンのSwiftはこれだけの手順を踏めば試すことができます。

これからもどんどんよりよくなっていくSwiftを追いかけていきましょう。