Swiftで遊ぼう! on Hatena

あしたさぬきblogでやってた初心者オヤジのiOSプログラミング奮闘記がHatenaに来ました

Swiftで遊ぼう! - 1017 - Xcodeにコメントを加える

最初っから難しいプロジェクトに取り組むことを諦めて非常に単純なプロジェクトに取り組んでいる。自分の子供向けの学習プログラムを作ってみようと思って、取り組み始めたわけ。

でも、コードを書いていても、やっぱりオヤジは、自分で書いたコードても後で見返すと分からなくなってしまう。Xcodeでコーディングしていても、詳しいコメントを残していかないと、後で振り返っても覚えてないことばかりになる。ということで、コメントの書き方は非常に重要なんだろう。書いたメソッドの説明をちゃんと記述して残していないと全く後で使えないってことになる。情けないことにXcodeにコメントを残すルールも知らなかった。

 /**
     角度を与えることで円周上の位置をCGPointで返すメソッド
     
     CGRectの長方形を与えて、その長方形の短辺から円を作り、そこにラジアン角度を与えることで、円周上の位置情報をCGPointで返す
     
     - important: 角度はラジアン角度を与える必要があるので、全外周長は2πである
     - returns: 外周の位置情報をCGPointとして返す
     - parameter radianAngle: 位置を得るために角度をラジアン角度として与える
     - parameters:
        - rect: 長方形を与えることでその短辺から円の半径をもとめる
*/
    private func topPosition(for radianAngle: CGFloat, in rect: CGRect) -> CGPoint {
     -
     -

メソッドの記述直前に、こういう風にコメントを書いて、メソッドを 「⌥ (Option) + クリック」してやると次のように説明用のウインドウが立ち上がる。

f:id:yataiblue:20200520071446p:plain

最初の行に書いた文章はSummaryに表示され、1行空欄を開けるとDiscussionに表示されるみたい。例に書いているようなタグを書いて記述すれば、それぞれの説明文が表示される。何年もXcodeを扱っていて、コメントの書き方も知らなかったとは。

Swiftで遊ぼう! - 1016 - カスタムシェイプ

時間があるときに少しずつ勉強しているので、忘れることも多い。でも、地道にやっていると少しずつではあるが確実に知識はついていっていると思うよ。

今日はSwiftUIで、カスタムシェイプの勉強をしてました。

SwiftUIでも、UIKitやSpriteKitでできることが基本的にできるので、カスタムシェープの扱いを勉強してました、どうやって円形を作るか基本的な知識の勉強です。

struct Circle: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        let width = rect.width
        let height = rect.height
        let posX = rect.origin.x
        let posY = rect.origin.y
        let center = CGPoint(x: posX + width / 2, 
    y: posY + posY + height / 2)
        let radius = width / 2
        
        path.addArc(center: center, radius: radius, 
      startAngle: .degrees(0), 
      endAngle: Angle(radians: 2.0 * Double.pi), 
      clockwise: true)
        path.closeSubpath()
        
        
        return path
    }
}

これを描画するためにradiansの因数、staratAngleやendAngleに変数を渡さなければならないんですが、そこでAngle型の変数が必要なんですよが、いくつかバリエーションがあるんです:

// Angleの型のパラメーターを指定して与える方法に
// 次のように直接パラメーターを指定する方法
.degree(30)
// そして角度を与えるパラメーターとして型から与える方法
Angle(degree: 30)
// 更に ラディアスを因数として与えると次のようになる
Angle(radians: 2 * Double.pi / 2 ))

そうなんです。どれも同じ結果になるんですよね。プログラミングのスタイルは1つじゃないってこと。こういうことってプログラマーにっとって基本的な知識なんですが、素人にとって混乱するところ。人生に1つの答えは無いとの同じようにプログラミングにも1つの答えは1つじゃないってこと。色々考えながら進んで行きましょう。

Swiftで遊ぼう! - 1015 - URLSessionDataTaskPublisherの基礎

URLSessionの基本をまとめたのが5年前...

yataiblue.hatenablog.com

5年経過したので、NSURLは消えてしまったようですが、URLSessionはネットからデータを取り込むために今でも利用されている。しかし、新しいフレームワークであるCombineと組み合わせて利用するのが一般的になってきている。Combineは「関数型プログラミング」を実現するためのフレームワークであり、SwiftUIと組み合わせることで、最も新しいiOS開発環境ということになるだろう。そこで、今回、Combineに関して少々勉強できたので、5年前のデモを新しく書き直してみた。

当初は、上記のリンクの記事を更新するつもりでしたが、SwiftUIとCombineを使って開発をすると、Autolayoutは使わないし、非同期処理も違うので、古い記事はそのままにして、新しい記事を書いてみた。

新しいプロジェクトを作ります。「Simple View Application」を選び、「User Interface」を必ず「SwiftUI」にする。そして、プロジェクト名は適当につけますが、私は「URLSessionDataTaskPublisherDemo」にしました。

SwiftUIの基本的な扱いに関して説明はしません。分からない人はSwiftUIのチュートリアルをこなしてください。SwiftUIの特徴は、MVC開発からMVVM開発への変化でないかと思ってるんですが、まだ私も勉強中で分からないことだらけ。ContentViewでユーザーインターフェイスの開発に集中。そして、Modelで処理させて、Combineなどの通知機能を利用することで自動的にViewと連携させてやるという考えじゃないだろうか。今まで使用していたUIViewContorollerも使いません。私の嫌いなAutolayoutも必要無いので、処理をどうすればいいか開発に集中できるようになっているので、これからiOS環境を勉強し始める人が羨ましいですね。

さて、画面上のボタンを押して、Amazonのサイトから画像をダウンロードさせるだけですが、その処理をContentViewには加えません。新しいModelになるファイルを作りますメニューから「File」→「New」→「File...」を選択します。そして「Swift File」を選択してから名前を「ImageLoader」にしてプロジェクト内に加えます。「import Foundation」しかないので、次のように「SwiftUI」と「Combine」を加えてください

import Foundation
import SwiftUI
import Combine

さて次に、アプリを立ち上げた時に表示するイメージを用意します。アプリのテスト環境は「iPhone 11」にします。すると、プロジェクトで使用するイメージのサイズは2種類でいいんです*1。先ず、プロジェクト・ナビゲータでAssets Xcassetsを選択して、下部にある「+」ボタンを選び、「New Folder」を選択します。フォルダーの名前を「Placeholder」に変更してから「+」ボタンを再び選択して「New Image Set」を選ぶと「1X」「2X」「3X」の空のスロットができます。

次に何でもいいので「png」イメージを用意します。オブジェクトのサイズを「240×240ピクセル」にしているので、1Xはそのまま、2Xは「480×480ピクセル」、そして3Xは「720×720ピクセル」の画像を用意するのですが、ここでは2Xスロットだけセットします。そして、名前を「backgroundImage」に変更します。

f:id:yataiblue:20200329143128j:plain

これで画像は用意できたので、ImageLoaderに初期値として、先ほど用意したbackgroundImageを持つUIImageを持たせるためにコードを次のように書きます。

import Foundation
import SwiftUI
import Combine

class ImageLoader: ObservableObject {
    @Published var image = UIImage(named: "backgroudImage")
    
}

ここで、2つの重要なキーワードが出てきました。ObservableObject@Publishedキーワードです。実はこのObservableObjectですが、Combineに含まれるんです。でも、Combineフレームワークをimportしなくても、SwiftUIのみで使えるため、最初に私は混乱しました。SwiftUIを使って、システム標準の通知機能を使うだけならCombineフレームワークは必要ないのです。外部との通信や、通知機能を自作するためにCombineが用意されています。また、非同期処理もCombineでさせる方が処理させやすいということですが、まだまだ私の知識で使いこなせるとは思ってません。

ここで用意したimgaeをContentViewで表示させてみます。次のコードをContentViewに加えます。

struct ContentView: View {
    @ObservedObject var loader = ImageLoader()
    
    var body: some View {
        VStack {
            Image(uiImage: loader.image!)
                .resizable()
                .frame(width: 240, height: 240)
                .scaledToFit()
        }
    }
}

そしてランしてみましょう。当然プレビューをみてもかまいません。次のようになっているでしょう。f:id:yataiblue:20200329151436p:plain
次にAmazonから画像データをダウンロードさせる関数をModelに用意させるのです。ここでCombineフレームワークを使います。Combineフレームワークを利用するために、Publisher、Subscriber、そしてOperatorを利用しなけらばなりません。自分で作るクラスなら、それぞれのProtocolやExtensionに準拠させないといけないのですが、URLSessionにはCombineが利用できるようになっています。URLSessionで用意されているPublisherがDataTaskPublisherなんです。このDataTaskPublisherを使ってSubsriberを作り、情報処理をOperatorでするっていう流れです。従って、DataTaskPublisherを利用すると、必ずData、URLResponnse、URLErrorの処理をしなければならないので、利用するしないに関係なく対応をしなければなりません。ということで、ImageLoaderクラスに次の変数を持たせます。

class ImageLoader: ObservableObject {
    @Published var image = UIImage(named: "backgroundImage")
    var cancellable: Cancellable? = nil
    var data: Data? = nil
    var response: URLResponse? = nil
    var error: URLError? = nil
    }
}

cancellableがSubscriberですが、Pubulisherの宣言がないですよね。Publisherはシングルトン利用するので、インスタンスは作らないことにします。

次の関数を加えます。

func fetchImage() {
        let url = URL(string: "https://s3.amazonaws.com/
              CoverProject/album/album_david_bowie_lets_dance.png")
        cancellable = URLSession.shared.dataTaskPublisher(for: url!)
            .print("subscriber now")
            .eraseToAnyPublisher()
            .receive(on: DispatchQueue.main)
            .sink(
                receiveCompletion: { completion in
                    switch completion {
                    case .finished:
                        break
                    case .failure(let error):
                        fatalError(error.localizedDescription)
                    }
            },receiveValue: {
                let image = UIImage(data: $0.data)!
                self.image = image
            })
    }

これで自分が持っているイメージがダウンロードされたイメージと変わります。では、ContentViewにボタンを加えましょう。

struct ContentView: View {
    @ObservedObject var loader = ImageLoader()
    
    var body: some View {
        VStack {
            Image(uiImage: loader.image!)
                .resizable()
                .frame(width: 240, height: 240)
                .scaledToFit()
            Button(action: loader.ftechImage,
                   label: { Text("Get Image") })
        }
    }
}

じゃあランしてボタンを押してみるといいでしょう。次のようにデビッド・ボウイのジャケットが今も変わらずダウンロードできますよ。
f:id:yataiblue:20200329155509p:plain
実はImageLoaderクラスにUIResponseやUIErrorのプロパティを持たせましたが、$0のデータしか使っていないので、流れてきたデータを殆ど捨ててます。そして、このときのデータの流れがコンソールに出るのですが、色々な情報が紛れていて興味深かったです。まだまだCombineの勉強が必要なので、もう少し勉強します。

*1:理由がわからなければSwiftで遊ぼう! - 249 - iOSの座標システム: 2020年3月 - Swiftで遊ぼう! on Hatenaで説明しているScaling値を確かめましょう。

Swiftで遊ぼう! - 1014 - Viewのライフサイクル

ふふふふふ、私のブログをお気に入りにしている皆さん、私、屋台ブルーが、iOS開発を完全に諦めてしまって、ここを放置していたと思うでしょう(その通りです)。

いやあ、皆さんの思っていたように、移り変わりの激しい開発環境についていけず、完全に放置状態になっていたのは事実です。本業の勉強も忙しく、年取って結婚したため、子どもの教育にも時間をとられ、プログラミングは二の次になっています。それでも、OOPやPOPなど、プログラミングの基本概念をある程度理解できていたので、SwiftUIが導入され、それを理解するための本を読んでもちゃんと理解できます。

SwiftUI 徹底入門

SwiftUI 徹底入門

この本は、Swift開発の初心者には非常に難しい内容だと思います。まだ完全に理解しているわけではありませんが、内容が理解できるということは、私も全くの初心者ということではないでしょう。

SwiftUIでの開発を勉強していますが、自分の勉強にあわせて古い記事を書き直しています。次の記事を書き直しました。

yataiblue.hatenablog.com

さあ、どこまで復習ができることか。

Swiftで遊ぼう! - 1013 - SwiftUIは、滅茶楽しい!

今年のWWDC2019は、本当に楽しかったですね!

実は、去年のWWDC2018、ほどんとチェックして無かったんです(^_^;) しかし、今年は新しいフレームワークが目白押し!中でもSwiftUIを知ると、今まで習得してきた開発スタイルが全く必要無い!と言えるほど、開発環境が変化してしまったと言えるでしょう。もう、ストーリーボード使ってユーザーインターフェイスのレイアウトを考えるなんて、時代遅れ!なんて言えるかも(^^;)

リアルタイムにプレビューで確認できる新しいプログラミングスタイルに急速にシフトすると思います。

新しいOSのβ版をインストールしました。

現在私の開発(勉強)環境は、

MacBook Pro 15-inch 2018:macOS Catalina 10.15 Beta 2
Xcode 11.0 beta 2

そして、SwiftUI Tutorials https://developer.apple.com/tutorials/swiftui/ をやりました。

プロトコール指向のプログラミングの理解が無いと、このチュートリアルは分からないと思いますが、Swift言語を理解していると分かるんじゃないかな。新しいCombineフレームワークや、UI Data Sourcesの変更点も勉強しました。SF Symbolsが導入されることで、洗練されたインターフェイスのデザインを簡単に構築することもできますね。

かなりワクワクしています。

よっしゃ!アプリを作るぞ!

Swiftで遊ぼう! - 1012 - プロトコール指向プログラミング(POP)の復習

詳解 SWIFT 第4版 を読むのもつまらなくなってきました。ただ、だらだらと記述されている内容を追いかけるのは退屈です。やっぱり少しでもSwift 5に関連した内容を読みたいので、平行して他の本にも手を出してしまいました。

ということで、プロトコール指向プログラミングの復習をしました。

yataiblue.hatenablog.com

すんなり頭に入りますね。

Swiftで遊ぼう! - 1011 - メモリーリーク!

WWDC2019 開催も間近なのに、詳解SWIT 4版を、読んでいるというのもピント外れかもしれない(^_^;)

関数オーバーロードを読んで、ジェネリックを読んでいると、以前の記事が気になり、ARCのチュートリアルを復習して、ジェネリック・ラッパーの復習もしました。すっかり忘却の彼方だった概念が戻ってきています。というか、改めて自分の記事を読むと、理解しやすいです。一生懸命ブログに残していたことが役に立っています。

ARCの基礎を復習して、メモリーリークを生じさせないようにしましょう。

yataiblue.hatenablog.com