20200806のSwiftに関する記事は14件です。

[XCode 11.6, Swift5.2.4] プログラム初心者の「コピペ前は動いていたのに!」

著者とプログラム環境は現在以下になります。
  • 本格的なプログラミングは初めて(昔HyperCardでチマチマ作ったことはある)
  • 各種アプリのマクロは理解できる程度
  • Swift歴は4ヶ月くらい、SwiftUI歴は1ヶ月くらい
  • Xcode 11.6
  • Swift5.2.4

コピペで動くと思っている初心者

それは私です。
動いているコードをコピペして、XCodeから怒られた事例を見ていきます。
もしこのドキュメントが、読者の何らかの助けになっていれば幸いです。

説明用にSwiftUIのコードを用意しました。
ファイル作成時に、以下の変更を加えたものになります。
1. Text("Hello, World")を作成しました。
2. 間の部分の説明を追加しました。

import SwiftUI

//(この部分を、Aと呼ぶ)

struct SwiftUIView: View {

    //(この部分を、Bと呼ぶ)

    var body: some View {
        Text("Hello, World!")

        //(この部分を、Cと呼ぶ)

    }

    //(この部分を、Bと呼ぶ)

}

//(この部分を、Aと呼ぶ)

struct SwiftUIView_Previews: PreviewProvider {

    //(この部分を、Bと呼ぶ)

    static var previews: some View {
        SwiftUIView()

        //(この部分を、Cと呼ぶ)

    }
}

//(この部分を、Aと呼ぶ)

import SwiftUI部分

  • 怒られた点
    • import Foundationのファイルで、enumや Array[Type]を置いていたが、Color型を使った瞬間怒られた。
  • 解っていなかった点
    • Color型を使うにはimport SwiftUIが必要だった。FoundationはColor型を使えない。

A部分

  • 特になし。なぜなら使ったことないから。
    • import Foundationのファイルでは、A部分にenumやArray[Type]を書いていたので、ここに書くと他のファイルでも変数が使えると思う(グローバル変数?)。
    • 変数が散り散りになるので、普通のSwiftUIでA部分は使わない方がいい。

B部分・C部分

ここはBとCで結局同じ原因で怒られているので、まとめて書きます。

  • B部分:怒られた点
    • ForEachを使うと怒られた。
    • Text()を使うと怒られた。
    • C部分で使えた文をコピペすると、変数が使えなくて怒られた。
  • C部分:怒られた点
    • for in ループを使うと怒られた。
    • if elseやswitch caseを使うと怒られた。ただしreturnを使うと大丈夫(Group{}で囲ってもいい)。
    • varを定義したら怒られた。@State varも同様。
    • selfをやたら勧められる
  • 解っていなかった点
    • B部分は構造体Structの中、C部分は変数var body: some viewの中にあるということ。
      • C部分はvarの中なので、新たにvarを定義することはできない。
      • B部分はStruct直下なのでvarを定義できる。
      • C部分のコードはViewを返さないといけない(some view)。
      • C部分ではViewを返すコードを繰り返すことができるForEachを使う。
        • var内でif elseを使うと考えると、それは無理でしょうと(個人的には)思う。
        • なので、C部分のif else内ではreturnを使って、Viewを返す(Group{}で囲っても良い)。
    • B部分で定義した変数を、C部分で扱うときにはselfをつける。
      • Cから見ると、自分が所属しているStructの変数を使うので、selfを使う。

initialize で怒られる(コードに問題ないのにbuildできない)

正直、原因はいろいろありますが、コードは問題ない(と思われる)のに、どうしてもbuildできない時は、Previewsから値が渡されていないのかもしれません。

  • 条件(上記コードを例とします)
    • B部分で、@State var hogehoge: <型>を使用しており、=で値を入れていない。
    • 他のファイルで、SwiftUIView(hogehoge: fugafuga)と変数渡しする必要がある。
  • 原因
    • SwiftUIView_Previewsから、SwiftUIView()に変数hogehogeの値を渡していない。
  • 方法
    • SwiftUIView_PreviewsのB部分で、fugafugaを作る。let fugafuga = xxxなど。
    • swiftUIView_PreviewsのC部分で、SwiftUIView (hogehoge: fugafuga)とする

Previewsはそのページのコードの結果だけ、作って見せてくれる。 そのため、他のページからの変数渡しやCoreDataなどとの連携はできない。
Previewsでは渡される値を作って(上記letの部分)、渡しておく必要がある。
これを忘れていると、コード的には問題ないのにinitializeで怒られてしまい、buildができないことになる。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Xcode の Swift Package : Unit テストのメソッド名の先頭は "test" 縛り

作業手順

Xcode Version 11.6 (11E708)
1. File → New → Swift Package で MyLibrary プロジェクトを作成。
2. MyLibraryTest.swift を変更
3. Command+U でテストを実行
スクリーンショット 2020-08-06 19.41.28.png
"noTest" は呼び出されていない。allTests に追加していけば実行してくれるのかと思いきや無視されていた。

Xcode では "test" で始まるメソッドを自動的に実行する。


ドキュメントの何処かにあるのかもしれないが探せなかった。忘れた頃に同じ事でハマるのでメモっておく。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SwiftでGoogleのMaterialDesignを簡単に使ってみる

今回使うライブラリ

今回使うのは、GoogleがデザインしたMaterialDesignです。
iOS(Swift,Objective-C)だけでなく、Android(Java,Kotolin),Web(HTML,CSS,JavaScript?)もあります。
(詳しくはこちら)
パーツは material.io/components から探せるので、気に入ったものを探してみてください。
その中でも今回はTextFieldを使いたいと思います。

注意書き

今回はあくまで簡単(機能する程度)に実装するだけですので、Usageに書かれていることとは違うと思いますが、しっかりゴリゴリに実装したい方は、この記事を読まずにUsageを熟読することをおすすめします。
※こんな感じで少し雑な記事になると思いますがお許しください。。。

Podライブラリ導入

・Podファイルの生成

$ pod init

・Podファイルに追加

pod 'MaterialComponents/TextFields'

・Podインストール

$ pod install

使い方

.workspaceファイルを開きます。

・ストーリーボードに普通のUITextFieldを配置します。
スクリーンショット 2020-08-06 12.38.23.png
スクリーンショット 2020-08-06 12.38.34.png

ViewController.swiftにMaterialComponents.MaterialTextFieldsをインポートします。

ViewController.swift
import MaterialComponents.MaterialTextFields

・先ほどStoryboardに配置したUITextFieldとコードを関連付けをします。

ViewController.swift
@IBOutlet weak var GoogleTextField: UITextField!

・Storyboardに戻り、TextFieldのカスタムクラスを、MDCTextFieldに変更します。
スクリーンショット 2020-08-06 12.38.47.png

・こんな感じで実装できました!
名称未設定2.001.jpeg
しっかり表示されました!

・背景にこんな感じの入力する項目みたいなのをつけたいという方は・・・
Simulator Screen Shot - iPhone 11 Pro - 2020-08-06 at 18.58.00.png

・Placeholderの項目を変更してみてください!
 スライド.001.jpeg

サンプルGIF

ezgif.com-video-to-gif (2).gif

サンプルプロジェクト

https://github.com/rea-sna/GoogleDesignSample

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

HtmlタグでStringを装飾する方法

  • htmlタグをStringで書いて
  • そのStringをData変換
  • NSMutableAttributedString(data:options:documentAttributes)で装飾する
    • options に documentType.html を指定
let str = "<i>testText</i>"
let stringWithBaseFont = NSString(format:"<span style=\"font-family: '-apple-system', 'HelveticaNeue'; font-size: \(22)\">%@</span>" as NSString, str)

stringWithBaseFont.data(using: String.Encoding.unicode.rawValue, allowLossyConversion: true)!

if let attributedString = try? NSMutableAttributedString(
    data: stringWithBaseFont.data(using: String.Encoding.unicode.rawValue, allowLossyConversion: true)!,
    options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue],
    documentAttributes: nil) {
    attributedString.addAttribute(.foregroundColor, value: UIColor.red, range: NSRange(location: 0, length: attributedString.length))
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dictionary<Key, Value>型

 Dictionary<Key, Value>型

Dictionary<Key, Value>型は、
キー:で分けて、[]で囲みます。

var screenSizesInInches: Dictionary<String, Double>
    = ["iPhone 7": 4.7, "iPhone 7 Plus": 5.2, "iPad Pro": 12.9]
var screenSizesInInches: Dictionary<String, Double>
    = ["キー": , "キー": , "キー": ]

スマホの、インチ(Inchi)数?

連想配列

-Dictionaryは、連想配列らしいです。

『連想配列』。 辞書(英語: dictionary)、ハッシュ(英語: hash)、マップ(英語: map)とも呼ばれる。

.

フツーの配列は、インデックス(=始点からのズレ。) で、値を特定。
連想配列では、キーによって、値を特定。

連想配列(Wikipedia)

 まとめ休憩?

-Dictionary(=辞書)型は、キーと値がペア。
-キーをもとに、値を特定。
-キーが他のものと被るのは、NG。(値はOK)

 コード ?

さっき書いたコードの続き。

Keyを指定して、値をGET

import UIKit

var screenSizesInInches: Dictionary<String, Double>
    = ["iPhone 7": 4.7, "iPhone 7 Plus": 5.2, "iPad Pro": 12.9]

screenSizesInInches["iPhone 7"] // 4.7
screenSizesInInches[4.7]        // No exact matches in call to subscript

という感じで、

-Keyを指定すると、値をGETできるけど、
-値を指定したら、下記のエラー。

No exact matches in call to subscript
「インデックスの呼び出しにて、マッチするもの存在しないよ」 と言われました?

『キーをもとに、値を特定』するのが、Dictionaryの仕事なので、
妥当な結果だと思います。?

値の削除

2パターン。

-nilを代入
-removeValue(forKey: "キー")

screenSizesInInches["iPhone 7"] = nil
screenSizesInInches.removeValue(forKey: "iPhone 7 Plus")
  // どちらでも可。

※waiting for key-inで、「キーの入力待ち」 的な感じで、
forKeyfor は、接続詞だと思ふ。

 おわりに

 PlayGround、シンプルで秀逸。

 参考サイト

【swift入門 文法編】辞書(Dictionary型)をマスターする

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RealmSwiftのMigration

個人での開発中に、モデル定義を変更したところ、マイグレーションしてくれというエラーが出たため、備忘録として残しておきます。

マイグレーションとは

モデルの定義を変更したときに、データを保持したまま新しいモデル定義に適応させることをいいます。

サンプル

エラー

Realm公式に載っているコードで説明します。
すでに定義されている下記のモデルを変更してみます。

class Person: Object {
    @objc dynamic var firstName = ""
    @objc dynamic var lastName = ""
    @objc dynamic var age = 0
}

以下のように変更しました。

class Person: Object {
    @objc dynamic var firstName = ""
//ここを消しました
    @objc dynamic var age = 0
}

マイグレーションの設定を行わず、これでシュミレーターを起動すると、下記のようなエラーが出て、うまく起動しません。

Error Domain=io.realm Code=10 "Migration is required due to the following errors:
(エラー文以下省略)

解決策

AppDelegate.swiftのdidFinishLaunchingWithOptionsの部分で、マイグレーションに関する記述をしていきます。公式のコードを参照。

AppDelegate.swift
// Inside your application(application:didFinishLaunchingWithOptions:)

let config = Realm.Configuration(
    // Set the new schema version. This must be greater than the previously used
    // version (if you've never set a schema version before, the version is 0).
    //(訳)新しいスキーマのバージョンを設定。以前使っていたバージョンよりも高くなければいけない。これまでバージョンの設定をしていなければ、初期のバージョンの値は0。
    schemaVersion: 1,

    // Set the block which will be called automatically when opening a Realm with
    // a schema version lower than the one set above
    //(訳)上記のものより低いスキーマバージョンでrealmを開くときに、自動的に呼び出されるようにブロックの設定をする。
    migrationBlock: { migration, oldSchemaVersion in
        // We haven’t migrated anything yet, so oldSchemaVersion == 0
//(訳)まだマイグレーションを行っていないので、oldSchemaVersion == 0。
        if (oldSchemaVersion < 1) {
            // Nothing to do!
            // Realm will automatically detect new properties and removed properties
            // And will update the schema on disk automatically
//(訳)Realmは新しいプロパティと削除されたプロパティを自動で検知します。そして、自動でディスク上のスキーマを更新する。
        }
    })

// Tell Realm to use this new configuration object for the default Realm
//(訳)default Realmに対して、新しい設定オブジェクトを使うように、Realmに指示する。
Realm.Configuration.defaultConfiguration = config

// Now that we've told Realm how to handle the schema change, opening the file
// will automatically perform the migration
let realm = try! Realm()

最低限、スキーマがRealmによって(自動的に)アップグレードされたことを示すために、空のブロックでバージョンを更新する必要がある。

まとめ

自分は、開発の環境で、モデルの変更を行ったらschemaVersionを変更してあげています。
間違えているところがあれば、ご指摘いただけますと幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[swift5]エラーUse of unresolved identifier 'FirebaseApp'の対処法

エラー内容

FacebookログインをFirebaseを用いて実装しようと思い、コードを書いている際に発生したエラーです。記述コードは以下の通り。

AppDelegate.swift
import UIKit
import Firebase  //このコードを記述

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.

        FirebaseApp.configure()  //このコードを記述
        return true
    }

FirebaseApp.configure()の箇所にUse of unresolved identifier 'FirebaseApp'というエラーが発生する。

環境▼
swift5
Xcode 11.6

原因

どうやらpod 'Firebase/Core'をインストールした際に、古いバージョンでインストールされていた模様。FirebaseAppはバージョン4以上でないと定義されていないらしい。

なので、pod 'Firebase/Core'のバージョンを更新すれば解決できそう!

対処法

①対象アプリのディレクトリ内に存在するPodfile.lockファイルを削除。
②ターミナルで以下のコードを実行し、podを最新状態へ更新する。(アプリのディレクトリ

ターミナル.
pod repo update
ターミナル.
pod update

③続いて依存関係を考慮したいので、podfileにはpod 'Firebase/Core'だけを記述して以下を実行。

ターミナル.
pod install

これで最新のpod 'Firebase/Core'と、関連のライブラリがインストールされるので、Xcodeで確認してみて下さい。エラーが解決されていると思います。

なお、一度最新のpodをインストールすればPodfile.lockに最新バージョンが固定される為、他のpodを入れても問題ないので安心して下さい!

最後に

今回のエラーはswift初学者が高確率で遭遇するエラーだと思われます。
是非参考にして下さい!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOS14での写真アプリへのアクセスについて

はじめに

※ 本記事はWWDC2020で発表された内容を元に作成しております。
※ 記事中の画像はWWDC2020での発表のものを引用させていただいております。

iOS14から写真アプリへのアクセス方法に更新があるため、今回調査してまとめてみました。

写真アプリへのアクセスレベルの指定

iOS14から写真アプリへの認証を確認する際にはアクセスレベルを指定することが必要になります。
これには新たに定義された列挙型のPHAccessLevelを用います。
これによりアクセスレベルを追加のみ.addOnly、もしくは読み込み/書き込み.readWriteに指定することができるようになりました。

スクリーンショット 2020-08-05 22.36.12.png

見た目の変化

iOS13以前では新規でアプリをリリースしてそのアプリ内で写真を撮った場合、単に写真アプリへのアクセスを許可するかどうかの表示となっていたと思います。

iOS14ではアクセスのレベルがより細かく設定できるようになりました。
これにより、

写真の追加のみ
 ├ 許可する
 └ 許可しない

読み込み/書き込み
 ├ 全ての写真を対象
 ├ 選択した写真のみ対象
 └ 許可しない

といった選択肢になりました。
PHAccessLevel.addOnlyにしていると左画像、.readWriteにしていると右画像のようになります。
スクリーンショット 2020-08-05 22.21.17.png スクリーンショット 2020-08-05 22.21.37.png

コードでの対応

iOS13まではPHPhotoLibrary.authorizationStatus()に引数はありませんでしたが、iOS14では下記コードの中段のように引数にアクセスレベルを宣言する必要があります。
PHAccessLevelを用いたコード

まとめ

iOS14からはPHAccessLevelの登場により写真アプリのアクセスに影響が現れるので、アプリの用途に応じて認証の.addOnly.readWriteを宣言する必要があります。
抜け漏れをせずに対応していきましょう。

参考

  1. Handle the Limited Photos Library in your app
  2. Photo library changes in iOS 14
  3. authorizationStatus() | Apple Developer Documentation
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOS14での写真アプリへのアクセスレベルについて

はじめに

※ 本記事はWWDC2020で発表された内容を元に作成しております。
※ 記事中の画像はWWDC2020での発表のものを引用させていただいております。

iOS14から写真アプリへのアクセス方法に更新があるため、今回調査してまとめてみました。

写真アプリへのアクセスレベルの指定

iOS14から写真アプリへの認証を確認する際にはアクセスレベルを指定することが必要になります。
これには新たに定義された列挙型のPHAccessLevelを用います。
これによりアクセスレベルを追加のみ.addOnly、もしくは読み込み/書き込み.readWriteに指定することができるようになりました。

スクリーンショット 2020-08-05 22.36.12.png

見た目の変化

iOS13以前では新規でアプリをリリースしてそのアプリ内で写真を撮った場合、単に写真アプリへのアクセスを許可するかどうかの表示となっていたと思います。

iOS14ではアクセスのレベルがより細かく設定できるようになりました。
これにより、

写真の追加のみ
 ├ 許可する
 └ 許可しない

読み込み/書き込み
 ├ 全ての写真を対象
 ├ 選択した写真のみ対象
 └ 許可しない

といった選択肢になりました。
PHAccessLevel.addOnlyにしていると左画像、.readWriteにしていると右画像のようになります。
スクリーンショット 2020-08-05 22.21.17.png スクリーンショット 2020-08-05 22.21.37.png

コードでの対応

iOS13まではPHPhotoLibrary.authorizationStatus()に引数はありませんでしたが、iOS14では下記コードの中段のように引数にアクセスレベルを宣言する必要があります。
PHAccessLevelを用いたコード

まとめ

iOS14からはPHAccessLevelの登場により写真アプリのアクセスに影響が現れるので、アプリの用途に応じて認証の.addOnly.readWriteを宣言する必要があります。
抜け漏れをせずに対応していきましょう。

参考

  1. Handle the Limited Photos Library in your app
  2. Photo library changes in iOS 14
  3. authorizationStatus() | Apple Developer Documentation
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swiftのstatic (初歩的)

この記事はstaticの使い方を簡単にまとめたもので、詳しい解説は明記されていません。

使い方

struct MyStructuer {
    let instanceProperty = "ABC"
    static let typeProperty = 123 //これだけ!!
}

変数などを定義する際に、直前にstaticをつけるだけです。

staticをつけた場合と、つけなかった場合の違いを見ていくと

struct MyStructuer {
    let instanceProperty = "ABC"
    static let typeProperty = 123 
}

mystructuer = MyStructuer() //インスタンスを生成
print(mystructuer.instanceProperty) //=> ABC
print(MyStructuer.typeProperty) //=> 123

どこが違うかというと、

staticをつけていない変数はMyStructuerクラスからインスタンスを生成し、そのインスタンスからプロパティを出力しています。
staticをつけている変数は、インスタンスは使わずにプロパティを出力しています。

これによってインスタンスを生成する手間が省け、どこからでも簡単にプロパティを呼び出すことができます。

しかし逆の操作はともにエラーになりますので注意してください。

print(MyStructuer.instanceProperty) //=> エラー
print(mystrctuer.typeProperty) //=> エラー

staticはクラスやメソッドにも同様にして使えるので、より快適なコードを目指して使いこなしたいですね!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Stack Viewは便利ですね?

 Stack Viewは便利ですね。

Swiftを始めて、3日目。
Stackについて学んだので、簡単にまとめ。

-【Swift】超便利StackViewの実践的使い方!まだAutoLayoutで消耗してるの?
-遅ればせながら UIStackView 入門

と言っても、他サイト引用です。?

-Stack

「レイアウトの修正が大変...」というAuto Layoutの欠点を解決してくれます。
 最低限のAutoLayoutの設定で済む、良心的な機能です。

 補足

右下に5個並んでるボタンについて、ちょっと補足。

スクリーンショット 2020-08-06 8.24.29.png

-Stackの削除

Embed In(一番右) にて、Unembed

スクリーンショット 2020-08-06 8.41.25.png

-Constraintの更新・削除

Resolve Auto Layout Issues(右から2番目) にて、
Update~ が更新。
Clear~ が削除。

Updateすると、上記画像のオレンジ色の「所定のConstraintからズレてるよ!」的な
警告が消える。

スクリーンショット 2020-08-06 8.54.13.png

-元のConstraintの位置に戻す

Update Frames(一番左)
(名称が "Return Frames" じゃない理由が謎。?)

※使用環境

 iOS8以前では、Stack View は使用できないみたいです。
 iOS9以上で利用可能。

 おまけ

View Controllerの召喚。
オブジェクトライブラリで、View Controllerと検索。
スクリーンショット 2020-08-06 8.20.05.png

Is Initial View Controllerのチェックを忘れずに。
Runしたときの、EntryPoint になります。(最初に始まる画面になる。)

※当たり前ですが、EntryPointの複数指定は無理なので、
他のView Controllerでのチェックは自動で消えてくれます?

スクリーンショット 2020-08-06 9.31.30.png

オブジェクトライブラリ表示。

-Command + shift + L  
(Option長押し、表示を維持)
(一回ドラッグ&ドロップすれば、あとは長押し続けなくても表示を維持)

68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3638333739352f31353632373235332d303566322d346532352d326538392d6165326438643839306134302e706e67.png

 終わりに。

ネイティブアプリ開発は、エミュレーターが楽しい。
PC画面上にスマホがあるという感動 ?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

String.split(separator:) で Xcode Previews がコケる

SwiftUIで画面を実装中していたらXcode Previewsで表示していたプレビューが突然できなくなった。

Diagnosticsを開くとこんなエラー

Compiling failed: global function '__designTimeString(_:fallback:)' requires that 'String.Element' (aka 'Character') conform to 'ExpressibleByStringLiteral'

エラーの行も表示されるので当該箇所を見たら大体こんな感じ

let str = ~
let array = str.split(separator: "\n") // ← ここでエラー発生
    .map(String.init)

どうやら"\n"(String Literal)がXcode Previews上だとCharacterとして認識されないらしい(なぜ?)

解決策

Characterであることを明示すれば良い。

let array = str.split(separator: "\n" as Character)
// or
let array = str.split(separator: Character("\n"))

参考

https://developer.apple.com/forums/thread/125065

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Udemyのswiftコース(英語のやつ)でiOSアプリ開発を学ぶ その3

勉強するコースはこれです:point_down:
https://www.udemy.com/course/ios-13-app-development-bootcamp/

進捗

セクション4の途中まで終了(全部で36セクション)
Xcodeの使い方など開発環境について実際の操作を学び、アプリをつくった:grinning:

最初はただ表示するだけのアプリ。二つ目でタップしてサイコロの目を出すアプリを開発する(ようだ)。

感想

前回の記事(Udemyのswiftコース(英語のやつ)でiOSアプリ開発を学ぶ その2)から2週間以上も間があいてしまった…
提出期限とかないしコース視聴いつでもできるし、Angela(先生)の優しさに甘えてしまった(オンラインコースあるある)
やはり絶対にこの曜日のこの時間にやるとか決まり事をつくって習慣化していかないとオンラインコースは続かないのだ(戒め)

何はともあれアプリをつくって自分のiPhoneで表示させるのは実に気分が良い。
エンジニアとしてはちいさな一歩だが、初めて開発をした人にとってはおおきな一歩だとおもう。

コース内の小ネタとしては、スマホアプリ黎明期に「I AM rich.という画面を表示させるだけのアプリで高額課金させて儲けるという詐欺同然の方法で稼いだ人がいた」という話が実に興味深かった。
そのアプリ持っているだけで金持ちの象徴のようなことだしアプリの内容は間違っていない(?)

開発について説明中にちょくちょく小ボケがあったり興味深い話をしたり、飽きない工夫がされているので飽き性の自分でもなんとかできている気がする。

今後の予定

日曜の夜23時頃と水曜の夜23時頃に記事を書くし、書けるようにコースを進める
(習慣化できるように具体的な予定を設定する:point_up:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOS のライブラリを自作して Carthage と CocoaPods で公開する。CI で自動化まで。

こんにちは。iOS エンジニアです。

というふうに名乗っていながら、iOS 向けのライブラリを作ったことがなかったので、やばいと思って作ってみました。
作成中の一連の流れを記事に残したいと思います。
(具体的なコードの書き方とかではないです。)

成果物

https://github.com/yuki0n0/WaveSlider/
以下解説するものは、こちらのライブラリを作成したときの情報に基づいています。

中身自体は大したものではないんですが、
ライブラリ公開の経験をしたかったという動機なのでお手柔らかに、、、
よかったら GitHub で Star ★ つけてね。

作成

1. プロジェクト作成

Framework を選択して、次の画面で選択して、プロジェクト作る。
プロジェクト作成スクリーンショット

2. コーディング

GitHub (もしくは他の git ホスティングサービスなど) にアップしたり、中身を実装したり、自由にやってください。

サンプルプロジェクトの用意

自分のためにも、利用者のためにも、 Example プロジェクトを用意しておくといいかもしれません。
Example フォルダを作成し、その中に通常の iOS アプリの Xcode プロジェクトを作成しているパターンが多いようです。
Example ではなく Demo という名称を使用しているライブラリもありました。

また、その際のライブラリ管理ファイルの書き方は、下記のようにローカルのパスを指定するように記述します。

Podfile
target 'Example' do
  use_frameworks!
  pod "WaveSlider", :path => "../"
end
Cartfile
git "./.."

開発注意ポイント

  • 公開したいクラスなどに public 修飾子をつける
  • Bundle を利用する際は
    Bundle.main... ではなく Bundle(for: WaveSlider.self)... といったように書き換える。
  • etc...

3. 周辺ファイル

README.md

いわずもがな用意しましょう。

LICENSE

LICENSE ファイルを用意しましょう。
CocoaPods でも紹介されている、下記サイトを参考にできます。
https://choosealicense.com/

また、GitHub 上から直接 LICENSE ファイルを作成する場合、
下記のような選択 & 編集画面が出てくるため、わかりやすいです。
GitHub LICENSE 選択

Shared をチェック

Product > Scheme > Manage Schemes... を開いて、
Shared のチェックをつけます。
自分の確認した限りデフォルトでチェックがついていました。
その場合、一度チェックを外してから付け直すといいと思います。

Shared をチェック

公開

バージョニングの前提

セマンティックバージョニング に基づいていれば問題なさそうです。
また、Carthage はセマンティックでも alpha rc などの文字列が続くものはサポートされていません

つまり、バージョン名(=タグ)は下記のような通常のものだけを利用していれば問題ないと思います。
冒頭に v などもつける必要はありません。
1.0.0 1.0.2 ... 1.1.0 ... 2.0.0 ...

CocoaPods

1. podspec を作成

下記コマンドで ライブラリ名.podspec ファイルを生成します。

pod spec create ライブラリ名

作成された下記のようなファイルを、書き換えていきます。
詳細なコメントが書いてあるため、そんなに困らないと思います。
困ったら公式ページを参考にしてもいいと思います。

WaveSlider.podspec
Pod::Spec.new do |spec|
  spec.name         = "WaveSlider"
  spec.version      = "1.0.0"
  ...
end

下記コマンドで確認し、エラーがなくなるまで修正を繰り返し、 git push します。

pod spec lint
# もし問題がなければ下記のような出力が表示されるとお見ます
# [ライブラリ名] passed validation.

2. Release を作成 (タグを付ける)

Release を作成しましょう。
Releaseを作成

3. 登録作業

# 登録
# 確認メールが届くのでリンクを開いて認証する
pod trunk register "メールアドレス" "名前"

# CocoaPods にライブラリの登録
# 場合によっては --allow-warnings オプションを付けてもいいかもしれません
pod trunk push ライブラリ名.podspec

これで終わりです!
こんな感じで作成したライブラリのページが作られていると思います!
https://cocoapods.org/pods/WaveSlider

Carthage

ほぼやることはないです!
基本的に公式ドキュメントもしっかり見れるといいですね。
https://github.com/Carthage/Carthage#supporting-carthage-for-your-framework

1. 確認作業

下記コマンドを実行して問題ないかを確認します。
成功した際 Carthage/Build ディレクトリに成果物ができますが、こちらは git に含める必要はないので .gitignore に追記しておくべきです。

carthage build --no-skip-current

# もし成功しない場合は下記のようなコマンドを実行すると解決の糸口になるかもしれないよ by 公式ドキュメント
xcodebuild -scheme SCHEME -workspace WORKSPACE build
xcodebuild -scheme SCHEME -project PROJECT build

2. Release を作成 (タグを付ける)

CocoaPods の方の手順と同じです。すでにやっているなら必要ないです。
---- ここまでで最低限の公開作業は終わりです! ----

3. バイナリを Release にアップロードしておく (手動)

GitHub の Release には、ファイルをアップロードしておける機能があります。
画像で言う Assets の部分です。
ReleaseのAssets
ここにビルド済みのバイナリをアップロードしておきます。
するとライブラリ利用者側はこのバイナリを利用できるため、ビルド時間を省略できるというメリットがあります。

下記コマンドを実行すると、 *.framework.zip が作成されます。

carthage build --archive

これを下記からアップロードすれば完了です。
Releaseにアップロード

補足

アップロードされているバイナリが「Swiftのバージョンが異なる」等の理由で利用できない場合があります。
その場合は Carthage 側が自動的に判断1して、バイナリは利用せずにソースコードからビルドして利用します。
強制的にバイナリを使用したくない場合は --no-use-binaries オプションを付与してコマンドを実行します。

4. バイナリアップロードを自動化

上記の手動で行った手順を、GitHub Actions で実行します。
トリガーは release が公開されたタイミングとします。
コードは下記から御覧ください。
https://github.com/yuki0n0/WaveSlider/blob/1.0.2/.github/workflows/release_published.yaml

on:
  release:
    types: [published]

jobs:
  ...

Swift Package Manager

Swift Package Manager の対応もしたいですが、時間があるときに気が向いらたらやります。

参考


  1. Carthage 0.20.0 以降 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む