【SwiftUI】Instagramのストーリーに画像をシェアする

# はじめに
インスタのストーリーに直接遷移するには`facebook-ios-sdk`を使用しないといけなくなりました
あまり情報がなかったので記事にしておきます

https://github.com/facebook/facebook-ios-sdk

# サンプルアプリ
![RPReplay_Final1698326400 (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/85a8459b-7baf-3a55-9c4d-ab750e40d1af.gif)

# 前提条件
- Facebook開発者アカウントが作成済み (作り方は[こちら](https://developers.facebook.com/docs/development/register))
- Facebook開発者アカウントでアプリを作成済み (作り方は[こちら](https://developers.facebook.com/docs/development/create-an-app))

https://developers.facebook.com/docs/ios/getting-started

スタートガイドを参考に以下を取得する
- アプリ名(これは自分で設定する)
- アプリID
- クライアントトークン

# インストール
`https://github.com/facebook/facebook-ios-sdk`をインストールします

![スクリーンショット 2023-10-26 22.13.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/55749752-c7a5-952c-4f5d-4f73cf472c15.png)

`FirebaseCore`だけ依存に追加すれば機能します。
![スクリーンショット 2023-10-26 22.14.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/b5938188-350a-762a-740b-c6a53879ac53.png)

# Info.plistの設定
![スクリーンショット 2023-10-26 22.17.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/48dd601a-5bfd-cc1f-882d-67a5e35fd085.png)

```Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>FacebookAppID</key>
	<string>ここに「アプリID」</string>
	<key>FacebookClientToken</key>
	<string>ここに「クライアントトークン」</string>
	<key>FacebookDisplayName</key>
	<string>ここに「アプリ名」</string>
	<key>LSApplicationQueriesSchemes</key>
	<array>
		<string>instagram-stories</string>
	</array>
</dict>
</plist>
```

# 実装
```DemoApp.swift
import SwiftUI
import UIKit
import FacebookCore

class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
    ) -> Bool {
        ApplicationDelegate.shared.application(
            application,
            didFinishLaunchingWithOptions: launchOptions
        )
        return true
    }

    func application(
        _ application: UIApplication,
        open url: URL,
        options: [UIApplication.OpenURLOptionsKey: Any]
    ) -> Bool {
        ApplicationDelegate.shared.application(
            application,
            open: url,
            sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String,
            annotation: options[UIApplication.OpenURLOptionsKey.annotation]
        )
    }
}

@main
struct DemoApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
```

```InstagramRepository.swift
public enum InstagramError: LocalizedError {
    case missingURLScheme
    case missingStickerImageData
    case couldNotOpenInstagram
}

public enum OptionsKey: String {
    case stickerImage = "com.instagram.sharedSticker.stickerImage"
    case backgroundImage = "com.instagram.sharedSticker.backgroundImage"
    case backgroundVideo = "com.instagram.sharedSticker.backgroundVideo"
    case backgroundTopColor = "com.instagram.sharedSticker.backgroundTopColor"
    case backgroundBottomColor = "com.instagram.sharedSticker.backgroundBottomColor"
    case contentURL = "com.instagram.sharedSticker.contentURL"
}

final class InstagramRepository {
    private let appID = "123456789" // ここは「アプリID」に置き換える

    private var urlScheme: URL? {
        URL(string: "instagram-stories://share?source_application=\(appID)")
    }

    func share(
        stickerImage: UIImage,
        backgroundTopColor: String,
        backgroundBottomColor: String,
        contentURL: URL
    ) async throws {
        guard let urlScheme else {
            throw InstagramError.missingURLScheme
        }
        var items: [String: Any] = [:]
        guard let stickerData = stickerImage.pngData() else {
            throw InstagramError.missingStickerImageData
        }
        items[OptionsKey.stickerImage.rawValue] = stickerData
        items[OptionsKey.backgroundTopColor.rawValue] = backgroundTopColor
        items[OptionsKey.backgroundBottomColor.rawValue] = backgroundBottomColor
        items[OptionsKey.contentURL.rawValue] = contentURL.absoluteString
        guard await UIApplication.shared.canOpenURL(urlScheme) else {
            throw InstagramError.couldNotOpenInstagram
        }
        let pasteboardOptions = [UIPasteboard.OptionsKey.expirationDate: Date().addingTimeInterval(60 * 5)]
        UIPasteboard.general.setItems([items], options: pasteboardOptions)
        await UIApplication.shared.open(urlScheme)
    }
}
```

# 使い方
```swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        Button {
            Task {
                guard let stickerImage = UIImage(named: "icon"),
                      let url = URL(string: "https://qiita.com/SNQ-2001")
                else { return }
                do {
                    try await InstagramRepository.shared.share(
                        stickerImage: stickerImage,
                        backgroundTopColor: "#000823",
                        backgroundBottomColor: "#09112F",
                        contentURL: url
                    )
                } catch {
                    print(error.localizedDescription)
                }
            }
        } label: {
            Text("シェア")
        }
    }
}
```

# おわり
インスタのストーリーからバズが生まれることが多いと思うのでこの方法は押さえときたいです
開発者アカウントさえつくってしまえば勝ちです

# 参考記事
https://developers.facebook.com/docs/ios/getting-started

https://developers.facebook.com/docs/instagram/sharing-to-stories#ios---

https://qiita.com/knou/items/ed107b20762966ab64f1