読者です 読者をやめる 読者になる 読者になる

Swift,Objective-Cプログラミング ~ iOS ~

Objective-C,Swift,Apple Watchなどのプログラミング

【iOS Swift入門 #299】[RxSwift]バインドできるプロパティを作成する

この記事を読んでわかること

  • RxSwift(RxCocoa)でUIプロパティを拡張すること

はじめに

RxSwift(RxCocoa)ではObservableのbind(to:)メソッドを使い、 ObservableとUI部品のプロパティをバインドすることができます。

例)テキストフィールドの入力をラベルにバインドする

textField.rx.text.asObservable()
    .bindTo(label.rx.text)
    .disposed(by: bag)

RxCocoaではUILabelでバインドできるプロパティは
textとattributedTextの2つしか用意されていません。
他のプロパティもバインドできるようにしたい場合、
どうすればいいのか?

UIプロパティを拡張する

例として、テキストフィールドの入力文字数が

  • 奇数の場合、ラベルの背景色を黄色にする
  • 偶数の場合、ラベルの背景色を青にする

をバインドを使って実現したいとします。

RxCocoaではUILabelのbackgroundColorプロパティは用意されていません。
rxからアクセスできるtextプロパティやattributedTextプロパティが
どのように作成されているか、実際のソースを見てみます。

UILabel+Rx.swift

    //
    //  UILabel+Rx.swift
    //  RxCocoa
    //
    //  Created by Krunoslav Zaher on 4/1/15.
    //  Copyright © 2015 Krunoslav Zaher. All rights reserved.
    //
    
    #if os(iOS) || os(tvOS)
    
    #if !RX_NO_MODULE
    import RxSwift
    #endif
    import UIKit
    
    extension Reactive where Base: UILabel {
        
        /// Bindable sink for `text` property.
        public var text: UIBindingObserver<Base, String?> {
            return UIBindingObserver(UIElement: self.base) { label, text in
                label.text = text
            }
        }
    
        /// Bindable sink for `attributedText` property.
        public var attributedText: UIBindingObserver<Base, NSAttributedString?> {
            return UIBindingObserver(UIElement: self.base) { label, text in
                label.attributedText = text
            }
        }
        
    }
    
    #endif

UIBindingObserverタイプのプロパティを作ってあげればよいとわかります。
ここではbackgroundColorという名前でプロパティを作成します。

UILabel+Sample.swiftという名前でファイルを作成しました。

    import RxSwift
    import RxCocoa
    
    extension Reactive where Base: UILabel{
        var backgroundColor: UIBindingObserver<Base, UIColor>{
            return UIBindingObserver(UIElement: self.base){
                label, backgroundColor in
                label.backgroundColor = backgroundColor
            }
        }
    }

これを使ってテキストフィールドとラベルをバインドします。

    import UIKit
    import RxSwift
    import RxCocoa
    
    class ViewController: UIViewController {
        @IBOutlet weak var textField: UITextField!
        @IBOutlet weak var label: UILabel!
        private let bag = DisposeBag()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            
            textField.rx.text.asObservable()
                .map(toColor(from:))
                .bindTo(label.rx.backgroundColor)
                .disposed(by: bag)
            
        }
        
        // 文字数からUIColorを計算する
        private func toColor(from text:String?) -> UIColor{
            let isEven = ((text ?? "").characters.count % 2 == 0)
            let color = isEven ? UIColor.blue : UIColor.yellow
            return color
        }
    }

サンプルアプリ

①アプリを起動します テキストフィールドの入力文字数は、0文字で偶数なので、ラベルの背景色は青となります。

f:id:fjswkun:20170428145005p:plain

②1文字入力します テキストフィールドの入力文字数は、1文字で奇数なので、ラベルの背景色は黄となります。

f:id:fjswkun:20170428145034p:plain

③もう1文字入力します。(合計2文字となります) テキストフィールドの入力文字数は、2文字で偶数なので、ラベルの背景色は青となります。

f:id:fjswkun:20170428145048p:plain

サンプルソースのダウンロード

SampleCreateBinder.zip - Google ドライブ

Swift

入門書籍

絶対に挫折しない iPhoneアプリ開発「超」入門 増補改訂第5版
プログラミングが初めて!という人が小さなアプリを作ることで、アプリ開発を学ぶことができます。
「Swiftだけでなく、プログラミング自体が初めてなんだけど、どの本が良い?」と聞かれたときには、
この書籍をおすすめしています。

本気ではじめるiPhoneアプリ作り Xcode 8.x+Swift 3.x対応 (ヤフー黒帯シリーズ)
アプリ開発からApp Storeへの公開までの一通りを学ぶことができます。
入門書を2冊、3冊を読んだあとでこの書籍を読むとかなりの実力アップを感じることができます。

ただし、一通り学ぶことができますが、プログラミング初めてでこの書籍を選ぶことはオススメできません。
最初の方の内容はプログラミング初心者には理解が難しく、そこで勉強をやめてしまう可能性がありそうだと感じます。

Swiftポケットリファレンス
辞書として1冊は持ってても良い。

仕事でやれるレベルになるために

初心者から仕事でやれるレベルになるためにオススメできる日本語書籍がみつかりませんでした。
英語は苦手でも、書籍に書かれているソースはやさしく、読み進めることができます。

The iOS Apprentice (英語サイト・英語書籍)
Swift Apprentice (英語サイト・英語書籍)

平均的プログラマーを超えるために

詳解Swift 第3版
Swift3の書籍。第1版、第2版にもお世話になっています。
Swiftの文法についてとても詳しく書いてあります。

Ray Wenderlich | Tutorials for iPhone / iOS Developers and Gamers
QiitaのSwiftに関する記事

【iOS Swift入門 #298】Swift3.1での変更点「Sequenceプロトコル(Array等)への関数追加」

この記事を読んでわかること

  • Swift3.1での変更点
  • prefix(while:)とdrop(while:)の使い方

Sequenceプロトコル(Array等)への関数追加

prefix(while:)とdrop(while:)が追加された。

prefix(while:)

先頭の要素から「条件を満たさなくなる」までの値の配列を返す。

例: 条件は「100未満の値」

100の時点で条件を満たさなくなる。 そのため、最後の5は返されない。

let nums = [1, 2, 3, 10, 20, 30, 100, 200, 300, 5]
let prefixed = nums.prefix(while: {$0 < 100})
// [1, 2, 3, 10, 20, 30]

例: 条件は「1文字以上」

先頭の要素が空文字で条件を満たさないため、 2番めの要素以降は返されない。

let words = ["", "A", "B", "C", "A1", "A2", "A3", "A1X", "A2X", "A3X"]
let words_prefixed = words.prefix(while: {x in x.characters.count > 0})
// []

drop(while:)

先頭の要素から「条件を満たす」要素をスキップする。 条件を満たさなくなった要素「以降」の配列を返す。

例: 条件は「100未満の値」

先頭から30までの要素は条件を満たすため、スキップされる。 それ以降の配列が返される。

let nums = [1, 2, 3, 10, 20, 30, 100, 200, 300, 5]
let dropped nums.drop(while: {$0 < 100})
// [100, 200, 300, 5]

例: 条件は「1文字以上」

先頭の要素が条件を満たさない。 全ての要素を含む配列が返される。

let words = ["", "A", "B", "C", "A1", "A2", "A3", "A1X", "A2X", "A3X"]
let words_prefixed = words.drop(while: {x in x.characters.count > 0})
// ["", "A", "B", "C", "A1", "A2", "A3", "A1X", "A2X", "A3X"]

Swift

入門書籍

絶対に挫折しない iPhoneアプリ開発「超」入門 増補改訂第5版
プログラミングが初めて!という人が小さなアプリを作ることで、アプリ開発を学ぶことができます。
「Swiftだけでなく、プログラミング自体が初めてなんだけど、どの本が良い?」と聞かれたときには、
この書籍をおすすめしています。

本気ではじめるiPhoneアプリ作り Xcode 8.x+Swift 3.x対応 (ヤフー黒帯シリーズ)
アプリ開発からApp Storeへの公開までの一通りを学ぶことができます。
入門書を2冊、3冊を読んだあとでこの書籍を読むとかなりの実力アップを感じることができます。

ただし、一通り学ぶことができますが、プログラミング初めてでこの書籍を選ぶことはオススメできません。
最初の方の内容はプログラミング初心者には理解が難しく、そこで勉強をやめてしまう可能性がありそうだと感じます。

Swiftポケットリファレンス
辞書として1冊は持ってても良い。

仕事でやれるレベルになるために

初心者から仕事でやれるレベルになるためにオススメできる日本語書籍がみつかりませんでした。
英語は苦手でも、書籍に書かれているソースはやさしく、読み進めることができます。

The iOS Apprentice (英語サイト・英語書籍)
Swift Apprentice (英語サイト・英語書籍)

平均的プログラマーを超えるために

詳解Swift 第3版
Swift3の書籍。第1版、第2版にもお世話になっています。
Swiftの文法についてとても詳しく書いてあります。

Ray Wenderlich | Tutorials for iPhone / iOS Developers and Gamers
QiitaのSwiftに関する記事

【iOS Swift入門 #297】Swift3.1での変更点「Intのイニシャライザが追加」

この記事を読んでわかること

  • Swift3.1での変更点
  • init?(exactly:)の使い方

Intのイニシャライザが追加

Swift3.1でInt型で下記イニシャライザが追加された。

init?(exactly:)

小数点以下の切り捨てや切上げ、四捨五入が必要なければ、 Intを返す。そうでなければ、nilを返す。
つまり、小数点以下が0以外であればnilが返ってくる。

いくつか例を書いた。

let num1 = Int(exactly: 1.0)  // 3のInt型
let num2 = Int(exactly: 2)  // 2のInt型
let num3 = Int(exactly: 3.1)  // nil
let num4 = Int(exactly: 4.01)  // nil
let num5 = Int(exactly: 5)  // 5のInt型

Swift

入門書籍

絶対に挫折しない iPhoneアプリ開発「超」入門 増補改訂第5版
プログラミングが初めて!という人が小さなアプリを作ることで、アプリ開発を学ぶことができます。
「Swiftだけでなく、プログラミング自体が初めてなんだけど、どの本が良い?」と聞かれたときには、
この書籍をおすすめしています。

本気ではじめるiPhoneアプリ作り Xcode 8.x+Swift 3.x対応 (ヤフー黒帯シリーズ)
アプリ開発からApp Storeへの公開までの一通りを学ぶことができます。
入門書を2冊、3冊を読んだあとでこの書籍を読むとかなりの実力アップを感じることができます。

ただし、一通り学ぶことができますが、プログラミング初めてでこの書籍を選ぶことはオススメできません。
最初の方の内容はプログラミング初心者には理解が難しく、そこで勉強をやめてしまう可能性がありそうだと感じます。

Swiftポケットリファレンス
辞書として1冊は持ってても良い。

仕事でやれるレベルになるために

初心者から仕事でやれるレベルになるためにオススメできる日本語書籍がみつかりませんでした。
英語は苦手でも、書籍に書かれているソースはやさしく、読み進めることができます。

The iOS Apprentice (英語サイト・英語書籍)
Swift Apprentice (英語サイト・英語書籍)

平均的プログラマーを超えるために

詳解Swift 第3版
Swift3の書籍。第1版、第2版にもお世話になっています。
Swiftの文法についてとても詳しく書いてあります。

Ray Wenderlich | Tutorials for iPhone / iOS Developers and Gamers
QiitaのSwiftに関する記事

【iOS Swift入門 #296】アプリアイコンを動的に変更する。サンプルあり(iOS10.3から可能)

この記事を読んでわかること

  • アプリアイコンを動的に変更する方法

アプリアイコンを動的に変更する方法のポイント

  • iOS10.3からアプリアイコンを動的に変更できるようになった
  • イコン画像は予めアプリバンドル内に入れておく必要がある。アプリからダウンロードした画像をアイコン画像に使うことはできなさそう!?
  • イコン画像変更したときには変更完了ダイアログがユーザーに表示される。なので、ユーザーにこっそり変更しておくことはできないよう

実装

  1. プロジェクトにアイコン画像を追加する
  2. info.plistにアイコン情報を追加する
  3. アイコンを変更するトリガーとなる処理を書く

1.プロジェクトにアイコン画像を追加する

PNG形式の画像が必要。PDF形式ではエラーになりました

下記2つのファイルを用意しました。 実際には、デバイスにより、@2xや@3xを用意するのがよいでしょう。

  • blueIcon.png (60*60) → デフォルトアイコン
  • brownIcon.png (60*60) → 変更するアイコン

プロジェクトに追加します。 f:id:fjswkun:20170313184840p:plain

2.info.plistにアイコン情報を追加する

<key>CFBundleIcons</key>
<dict>
    <key>CFBundleAlternateIcons</key>
    <dict>
        <key>brownIcon</key>
        <dict>
            <key>CFBundleIconFiles</key>
            <array>
                <string> brownIcon </string>
            </array>
        </dict>
    </dict>
    <key>CFBundlePrimaryIcon</key>
    <dict>
        <key>CFBundleIconFiles</key>
        <array>
            <string>blueIcon</string>
        </array>
    </dict>
</dict>

Xcode上で見ると下記のようになります。 f:id:fjswkun:20170313185412p:plain

3.アイコンを変更するトリガーとなる処理を書く

ボタンをタップすると、
  blueIcon ↔ brownIcon と変更するようにしました。

UIApplicationクラスのsetAlternateIconNameメソッドを使って変更します。

詳細は公式の情報を確認しましょう。 setAlternateIconName(_:completionHandler:) - UIApplication | Apple Developer Documentation

@IBAction func changeIcon(_ sender: UIButton) {
    
    // デフォルトアイコンの場合、brownIconに変更する
    // brownIconの場合、デフォルトアイコンに変更する
    var nextIconName:String? = nil
    if let _ = UIApplication.shared.alternateIconName{
        nextIconName = nil
    }else{
        nextIconName = "brownIcon"
    }
    
    UIApplication.shared.setAlternateIconName(nextIconName){
        (error) in
        print(error?.localizedDescription ?? "description is empty")
    }
}

サンプル

サンプルの概要

アプリインストール時のアイコン f:id:fjswkun:20170313185826p:plain

アプリ起動します。 f:id:fjswkun:20170313185853p:plain

ボタンをタップします。 f:id:fjswkun:20170313185905p:plain 完了ダイアログが表示されます。

アプリをバックグラウンドにします。 アイコンが変更されています。 f:id:fjswkun:20170313185941p:plain

サンプルソースのダウンロード

SampleChangeIcon.zip - Google ドライブ

なお、iOS SDK10.3以上でないとアプリアイコンの変更はできないので注意を。

Swift

入門書籍

絶対に挫折しない iPhoneアプリ開発「超」入門 増補改訂第5版
プログラミングが初めて!という人が小さなアプリを作ることで、アプリ開発を学ぶことができます。
「Swiftだけでなく、プログラミング自体が初めてなんだけど、どの本が良い?」と聞かれたときには、
この書籍をおすすめしています。

本気ではじめるiPhoneアプリ作り Xcode 8.x+Swift 3.x対応 (ヤフー黒帯シリーズ)
アプリ開発からApp Storeへの公開までの一通りを学ぶことができます。
入門書を2冊、3冊を読んだあとでこの書籍を読むとかなりの実力アップを感じることができます。

ただし、一通り学ぶことができますが、プログラミング初めてでこの書籍を選ぶことはオススメできません。
最初の方の内容はプログラミング初心者には理解が難しく、そこで勉強をやめてしまう可能性がありそうだと感じます。

Swiftポケットリファレンス
辞書として1冊は持ってても良い。

仕事でやれるレベルになるために

初心者から仕事でやれるレベルになるためにオススメできる日本語書籍がみつかりませんでした。
英語は苦手でも、書籍に書かれているソースはやさしく、読み進めることができます。

The iOS Apprentice (英語サイト・英語書籍)
Swift Apprentice (英語サイト・英語書籍)

平均的プログラマーを超えるために

詳解Swift 第3版
Swift3の書籍。第1版、第2版にもお世話になっています。
Swiftの文法についてとても詳しく書いてあります。

Ray Wenderlich | Tutorials for iPhone / iOS Developers and Gamers
QiitaのSwiftに関する記事

【iOS Swift入門 #295】アプリ起動中(フォアグラウンド)でも通知(ローカル・リモート)を表示する

この記事を読んでわかること

  • アプリ起動中に通知(ローカル・リモート)を表示する方法

はじめに

  • iOS10以前はアプリがフォアグラウンドの時に通知を受信しても、通知は表示されない
  • iOS10からフォアグラウンドでも通知が表示できるようになった
  • ただし、通知に関するソースを変更する必要がある

方法

iOS10からUserNotificationsフレームワークが追加されました。
このフレームワークのUNUserNotificationCenterDelegateの実装が必要です。

具体的には
userNotificationCenter(_:willPresent:withCompletionHandler:)
実行時にcompletionHandlerを実行する際に、引数を指定します。

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    // アプリ起動中でもアラート&音で通知
    completionHandler([.alert, .sound])
}

サンプル

ボタンが一つだけある画面です。
ボタンをタップすると5秒後に通知を受信します。 f:id:fjswkun:20170311121023p:plain

ボタンタップ後、アプリをフォアグラウンドにしたまま待ちます。
通知が表示されます。
f:id:fjswkun:20170311121140p:plain

サンプルのソースは下記URLから取得できます。
SamplePushInForeground.zip - Google ドライブ

AppDelegate

import UIKit
import UserNotifications

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        // 通知許可の取得
        UNUserNotificationCenter.current().requestAuthorization(
        options: [.alert, .sound, .badge]){
            (granted, _) in
            if granted{
                UNUserNotificationCenter.current().delegate = self
            }
        }
        
        return true
    }
}

extension AppDelegate:UNUserNotificationCenterDelegate{
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        // アプリ起動中でもアラート&音で通知
        completionHandler([.alert, .sound])
    }
    
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        completionHandler()
    }
}

ViewController

import UIKit
import UserNotifications

class ViewController: UIViewController {
    
    @IBAction func tapButton(_ sender: UIButton) {
        let contents = UNMutableNotificationContent()
        contents.title = "Hello!!!"
        contents.subtitle = "Bye!!!"
        contents.body = "Good night!!! \(Date().description(with: NSLocale.system))"
        
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
        let identifier = NSUUID().uuidString
        let request = UNNotificationRequest(identifier: identifier, content: contents, trigger: trigger)
        UNUserNotificationCenter.current().add(request){
            error in
            print(error?.localizedDescription)
        }
    }
}

Swift

入門書籍

絶対に挫折しない iPhoneアプリ開発「超」入門 増補改訂第5版
プログラミングが初めて!という人が小さなアプリを作ることで、アプリ開発を学ぶことができます。
「Swiftだけでなく、プログラミング自体が初めてなんだけど、どの本が良い?」と聞かれたときには、
この書籍をおすすめしています。

本気ではじめるiPhoneアプリ作り Xcode 8.x+Swift 3.x対応 (ヤフー黒帯シリーズ)
アプリ開発からApp Storeへの公開までの一通りを学ぶことができます。
入門書を2冊、3冊を読んだあとでこの書籍を読むとかなりの実力アップを感じることができます。

ただし、一通り学ぶことができますが、プログラミング初めてでこの書籍を選ぶことはオススメできません。
最初の方の内容はプログラミング初心者には理解が難しく、そこで勉強をやめてしまう可能性がありそうだと感じます。

Swiftポケットリファレンス
辞書として1冊は持ってても良い。

仕事でやれるレベルになるために

初心者から仕事でやれるレベルになるためにオススメできる日本語書籍がみつかりませんでした。
英語は苦手でも、書籍に書かれているソースはやさしく、読み進めることができます。

The iOS Apprentice (英語サイト・英語書籍)
Swift Apprentice (英語サイト・英語書籍)

平均的プログラマーを超えるために

詳解Swift 第3版
Swift3の書籍。第1版、第2版にもお世話になっています。
Swiftの文法についてとても詳しく書いてあります。

Ray Wenderlich | Tutorials for iPhone / iOS Developers and Gamers
QiitaのSwiftに関する記事

【iOS Swift入門 #294】Zipファイルの圧縮・解凍チュートリアル(Tutorial)

はじめに

iOS Swiftを使ったZipファイルの圧縮・解凍のTutorial(チュートリアル)です。

この記事を読んでわかること

  • SwiftでZipファイルを圧縮・解凍する方法
  • ライブラリSSZipArchiveの使い方

Zipファイルの圧縮・解凍に使うライブラリ

SSZipArchiveというライブラリを使います。
Github上でStarやForkの数が多く、更新頻度も多いので安心です。

以下ではSwiftでコードを書いていますが、
このLibraryはObjective-Cでも利用できます。

プロジェクトへの追加方法

具体的な追加方法は公式ページを参照してください。

  • CocoaPods
  • Carthage
  • ソースをプロジェクトに追加

で追加することができます。

解凍

SSZipArchiveクラスのunzipFile:atPath:toDestinationメソッドを使う。
解凍するためのメソッドは他にも用意されていますが、
詳細は公式ページやソースを確認してください。

引数に解凍するZipファイルのパスと、解凍先フォルダのパスを渡します。

// path for zip file
let atZipFilePath = fileURLInBundle(name: "sample", ofType: "zip")!.path

// destination path to unzip
let unZipFilePath = documentDirectory.path

// exec unzip
SSZipArchive.unzipFile(atPath: atZipFilePath, toDestination: unZipFilePath)

圧縮

SSZipArchiveクラスのcreateZipFile:atPath:withFilesAtPathsメソッドを使う。
圧縮するためのメソッドは他にも用意されていますが、
詳細は公式ページやソースを確認してください。

引数に圧縮したZipファイルの格納先パスと、圧縮対象のファイルパス(配列)を渡します。

// zip file path
let atZipFilePath = documentDirectory.appendingPathComponent("files.zip").path

// files containing into zip file
let targetFile = fileURLInBundle(name: "sample_text", ofType: "txt")!.path
let targetFile2 = fileURLInBundle(name: "sample_text2", ofType: "txt")!.path

// exec zip
SSZipArchive.createZipFile(atPath: atZipFilePath, withFilesAtPaths: [targetFile, targetFile2])

サンプル

↓からサンプルプロジェクトをダウンロードし確認してみてください。
SampleZipArchiveTutorial.zip - Google ドライブ

ボタンを押したときにコンソールに圧縮先、解凍先のパスを出力しています。
シミュレーターでボタンを押し、出力されたパスをMacで開き、結果を確認しましょう。

  • 圧縮 * f:id:fjswkun:20161210180341p:plain

f:id:fjswkun:20161210180354p:plain

  • 解凍 * f:id:fjswkun:20161210180439p:plain

f:id:fjswkun:20161210180447p:plain

import UIKit
import SSZipArchive

class ViewController: UIViewController {
    @IBOutlet weak var zippedFilePathLabel: UILabel!
    @IBOutlet weak var unzippedFilePathLabel: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func tapZipbutton(_ sender: UIButton) {
        clearDocumentDirectory()
        
        // zip file path
        let atZipFilePath = documentDirectory.appendingPathComponent("files.zip").path

        // files containing into zip file
        let targetFile = fileURLInBundle(name: "sample_text", ofType: "txt")!.path
        let targetFile2 = fileURLInBundle(name: "sample_text2", ofType: "txt")!.path

        // exec zip
        SSZipArchive.createZipFile(atPath: atZipFilePath, withFilesAtPaths: [targetFile, targetFile2])
        
        // show result
        zippedFilePathLabel.text
            = FileManager.default.fileExists(atPath: atZipFilePath)
            ? "created zipfile"
            : "creating zipfile failed"
        
        print(atZipFilePath)
    }
    
    @IBAction func tapUnzipButton(_ sender: UIButton) {
        clearDocumentDirectory()
        
        // path for zip file
        let atZipFilePath = fileURLInBundle(name: "sample", ofType: "zip")!.path

        // destination path to unzip
        let unZipFilePath = documentDirectory.path

        // exec unzip
        SSZipArchive.unzipFile(atPath: atZipFilePath, toDestination: unZipFilePath)
        
        
        // show result
        unzippedFilePathLabel.text
            = FileManager.default.fileExists(atPath: unZipFilePath)
            ? "unzipped"
            : "failed unzip"
        
        print(unZipFilePath)
    }
    
    // file url in main bundle
    private func fileURLInBundle(name:String?, ofType:String?) -> URL?{
        guard let path = Bundle.main.path(forResource: name, ofType: ofType) else{
            return nil
        }
        return URL(fileURLWithPath: path)
    }
    
    // documentFirectory's url
    private let documentDirectory:URL = {
        return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    }()
    
    // clear all files in documentDirectory
    private func clearDocumentDirectory(){
        do{
            let fileURLs = try FileManager.default.contentsOfDirectory(at: documentDirectory, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles, .skipsPackageDescendants, .skipsSubdirectoryDescendants])
            
            for url in fileURLs{
                try FileManager.default.removeItem(at: url)
            }
        }catch(let ex){
            print(ex.localizedDescription)
        }
    }
}

参考情報

Zip

Swift

入門書籍

絶対に挫折しない iPhoneアプリ開発「超」入門 増補改訂第5版
プログラミングが初めて!という人が小さなアプリを作ることで、アプリ開発を学ぶことができます。
「Swiftだけでなく、プログラミング自体が初めてなんだけど、どの本が良い?」と聞かれたときには、
この書籍をおすすめしています。

本気ではじめるiPhoneアプリ作り Xcode 8.x+Swift 3.x対応 (ヤフー黒帯シリーズ)
アプリ開発からApp Storeへの公開までの一通りを学ぶことができます。
入門書を2冊、3冊を読んだあとでこの書籍を読むとかなりの実力アップを感じることができます。

ただし、一通り学ぶことができますが、プログラミング初めてでこの書籍を選ぶことはオススメできません。
最初の方の内容はプログラミング初心者には理解が難しく、そこで勉強をやめてしまう可能性がありそうだと感じます。

Swiftポケットリファレンス
辞書として1冊は持ってても良い。

仕事でやれるレベルになるために

初心者から仕事でやれるレベルになるためにオススメできる日本語書籍がみつかりませんでした。
英語は苦手でも、書籍に書かれているソースはやさしく、読み進めることができます。

The iOS Apprentice (英語サイト・英語書籍)
Swift Apprentice (英語サイト・英語書籍)

平均的プログラマーを超えるために

詳解Swift 第3版
Swift3の書籍。第1版、第2版にもお世話になっています。
Swiftの文法についてとても詳しく書いてあります。

Ray Wenderlich | Tutorials for iPhone / iOS Developers and Gamers
QiitaのSwiftに関する記事

【iOS Swift入門 #293】iOS10 画像添付したローカル通知を送信・受信する

f:id:fjswkun:20161202204331p:plain

はじめに

画像添付したローカル通知を送信・受信する処理を確認します。

これ以降のサンプルは下記記事のサンプルの続きです。
そもそも、iOS10のローカル通知について初めてという場合は↓参照。 swift.swift-studying.com

実装

UNMutableNotificationContentオブジェクトの
attachmentsプロパティに画像のパスを追加してあげればよい。

ローカル通知に画像を添付

コメント「contentsに追加」で

  • 画像のダウンロード
  • 通知内容への画像の添付

を行っています。

アプリ起動し、ボタンタップ
f:id:fjswkun:20161202195045p:plain

ホーム画面で待つと通知
f:id:fjswkun:20161202203548p:plain

3D Touchをすると詳細表示
f:id:fjswkun:20161202211451p:plain

import UIKit
import UserNotifications

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let button = UIButton(type: .system)
        button.setTitle("通知追加", for: .normal)
        button.sizeToFit()
        button.center = view.center
        button.addTarget(self, action: #selector(addNotification), for: .touchUpInside)
        view.addSubview(button)
        
        // 通知許可の取得
        requestPermission()
    }
    
    func addNotification(){
        // コンテンツの作成
        let content = UNMutableNotificationContent()
        content.title = "This is title."
        content.subtitle = "This is subtitle."
        content.body = "This is body. \nHello world!"
        
        // 通知の登録
        func registNotification(with content:UNMutableNotificationContent){
            // トリガーの作成(5秒後に通知実行)
            let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10.0, repeats: false)
            
            // リクエストの作成
            let identifier = NSUUID().uuidString
            let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
            
            // リクエスト実行
            UNUserNotificationCenter.current().add(request){
                error in print(error?.localizedDescription)
            }
        }
        
        // contentsに追加
        download(url: "http://cdn-ak.f.st-hatena.com/images/fotolife/f/fjswkun/20151005/20151005235737.png",
                 success: { url in
                    do{
                        let attachment = try UNNotificationAttachment(identifier: UUID().uuidString, url: url, options: nil)
                        content.attachments.append(attachment)
                    }catch{}
                    
                    defer{
                        registNotification(with: content)
                    }
        },
                 failure: { error in
                    print(error)
                    registNotification(with: content)
        })
    }
    
    // ダウンロード処理
    private func download(url:String, success:((_ url:URL) -> Void)?, failure:((_ error:Error) -> Void)?){
        
        guard let url = URL(string: url) else{
            failure?(NotificationServiceError.invalidURLError)
            return
        }
        
        let session = URLSession(configuration: .default)
        session.downloadTask(with: url){
            url, response, error in
            
            if let error = error{
                failure?(error)
                return
            }
            
            guard let url = url else{
                failure?(NotificationServiceError.notExistsContent)
                return
            }
            
            let copyURL = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!).appendingPathComponent("image.png")
            do{
                try FileManager.default.copyItem(at: url, to: copyURL)
            }
            catch(let error){
                failure?(error)
            }
            success?(copyURL)
            }.resume()
    }
    
    private enum NotificationServiceError:Error {
        case invalidURLError
        case notExistsContent
    }

    private func requestPermission(){
        UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .alert, .sound]){
            granted, errror in
            
            if granted{
                print("notification permission granted")
            }else{
                print("notification permission denied")
            }
        }
    }
}

サンプルのダウンロード

こちらからダウンロード。
SampleLocalNotification.zip - Google ドライブ

参考情報

Swift

iOSアプリ開発未経験の人向け
・Swift初心者の人に評判が良いようですね


・わかりやすくていい、と聞いた本
立ち読みした感じだと【アプリ作成未経験の人向け】

絶対に挫折しない iPhoneアプリ開発「超」入門【Swift & iOS8.1以降 完全対応】

絶対に挫折しない iPhoneアプリ開発「超」入門【Swift & iOS8.1以降 完全対応】


②を終えたくらいの知識の方向け
・アプリ開発独学中に2冊めに購入した書籍
プログラミング知識はなくても、若干理解するのに時間がかかる程度で、
読み進められるわかりやすさがある。

iPhoneアプリ開発塾

iPhoneアプリ開発塾


③中級者、上級者向け

詳解 Swift

詳解 Swift

詳解 Swift 改訂版

詳解 Swift 改訂版