Open15
備忘録
たまに使う環境変数一覧
@Environment (\.presentationMode) private var presentationMode
@Environment(\.openURL) private var openURL
以下の記事で事済む。
Label
Label(
title: { Text("Jude Bellingham") },
icon: {
Image(systemName: "22.circle.fill")
.foregroundStyle(.orange)
}
)
overlay & background
Circleは高さを持たない点に注意する。
Text("Sample")
.overlay {
Circle()
.foregroundStyle(Color(uiColor: .systemGray6))
.scaledToFill()
}
Divider()
.padding(.vertical)
Text("Sample")
.background {
Circle()
.foregroundStyle(Color(uiColor: .systemGray6))
.scaledToFill()
}
よく使うボタンの装飾
paddingが二つあるのは少々気持ち悪いが、サイズに合わせ自動的に調整してくれる。RoundedRectangleをclipShapeの引数に持たせたいが、CGFloatかCGSizeを指定する必要があるので、Capsuleで妥協。colorInvertを用いることでダークモードにも対応できる。
Text("Sample")
.foregroundStyle(.primary)
.colorInvert()
.font(.title)
.fontWeight(.semibold)
.padding()
.padding(.horizontal)
.background(.blue)
.clipShape(Capsule())
Structure: StrideTo, StrideThrough
let strideTo = stride(from: 0, to: 10, by: 2)
strideTo.forEach { print($0, terminator: " ") } // 0 2 4 6 8
print("\n- - -")
let strideThrough = stride(from: 0, through: 10, by: 2)
strideThrough.forEach { print($0, terminator: " ")} // 0 2 4 6 8 10
TimelineView
TimelineView(.periodic(from: .now, by: 1)) { context in
Text(context.date.description)
.font(.title2)
}
Modeifier: redacted
Apple Watchのアプリ作成の時だったか、ロック画面ウィジェットの作成だったかで使った気がする。
Modifier: textcase
ListのSectionのheaderの英文字は自動的に全てが大文字となる。大文字も小文字も自由に使用したいときはこのモディファイアを用いる。ちなみにnilを入れてもOK。
ContentUnavailableView
struct ContentView: View {
var body: some View {
ContentUnavailableView("該当者なし", systemImage: "person.slash", description: Text("条件を変更し再度お試しください。"))
ContentUnavailableView.search
ContentUnavailableView.search(text: "apple")
ContentUnavailableView(label: {
Label("No Mail", systemImage: "tray.fill")
}, description: {
Text("New mails you receive will appear here.")
}, actions: {
Button(action: {}) {
Text("Refresh")
}
})
}
}
Modifier: textSelection
テキストのコピーを可能にする。部分選択は不可。
Text("Sample")
.textSelection(.enabled)
後日検証
/// SwiftDataのプレビューに関して
/// すでに保存されているものしか引数に取れない疑惑。
import SwiftUI
import SwiftData
@Model final class User {
let name: String
init(name: String) { self.name = name }
}
struct SampleView: View {
let user: User
var body: some View {
Text(user.name)
}
}
///// NGパターン
//#Preview {
// SampleView(user: .init(name: "Naruto"))
//}
/// OKパターン
#Preview {
let config = ModelConfiguration(isStoredInMemoryOnly: true)
let container = try! ModelContainer(for: User.self, configurations: config)
let naruto = User(name: "Naruto")
container.mainContext.insert(naruto)
return SampleView(user: naruto)
.modelContainer(container)
}
///// それとSwiftDataのモデルはどのような場合でもイニシャライザは必要っぽい。エラーメッセージは以下の通り。
///// @Model requires an initializer be provided for 'User'
//@Model final class User {
// let name: String = "Naruto"
//}
なぜかsharedModelContainerをAppのところで作ってるとOK。
ContentViewに何もつけてないけど問題ない。
import SwiftUI
import SwiftData
@main
struct SwiftData_240906App: App {
var sharedModelContainer: ModelContainer = {
let schema = Schema([
User.self,
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
import SwiftUI
import SwiftData
@Model final class User {
let name: String
init(name: String) { self.name = name }
}
struct SampleView: View {
let user: User
var body: some View {
Text(user.name)
}
}
/// OK
#Preview {
SampleView(user: .init(name: "Naruto"))
.modelContainer(for: User.self, inMemory: true)
}
そしてAppファイルにあれこれ書かない場合、以下のようにするとOK。
#Preview {
let config = ModelConfiguration(isStoredInMemoryOnly: true)
let container = try! ModelContainer(for: User.self, configurations: config)
return SampleView(user: .init(name: "Naruto"))
.modelContainer(for: User.self, inMemory: true)
}
ProcessInfo.processInfo.environment["UI_TESTING"] == "1"
ざっくりFirebaseAuth
import Foundation
import FirebaseAuth
@MainActor
final class AccountManager: ObservableObject {
@Published private(set) var currentUser: FirebaseAuth.User? {
didSet { print("currentUserが\(String(describing: oldValue))から\(String(describing: currentUser))に変更されました。\n")}
}
@Published private var auth: FirebaseAuth.Auth! {
didSet { print("authが\(String(describing: oldValue))から\(String(describing: auth))に変更されました。\n") }
}
var isReady: Bool { auth != nil }
private var authStateDidChangeListenerHandle: AuthStateDidChangeListenerHandle!
init() {
authStateDidChangeListenerHandle = Auth.auth().addStateDidChangeListener { [weak self] auth, currentUser in
if self?.auth != auth { self?.auth = auth }
if self?.currentUser != currentUser { self?.currentUser = currentUser }
}
}
deinit {
if let authStateDidChangeListenerHandle = authStateDidChangeListenerHandle {
Auth.auth().removeStateDidChangeListener(authStateDidChangeListenerHandle)
}
}
func createAccount(email: String, password: String) async throws {
try await auth.createUser(withEmail: email, password: password)
}
func deleteAccount(email: String, password: String) async throws {
let credential = EmailAuthProvider.credential(withEmail: email, password: password)
try await auth.currentUser?.reauthenticate(with: credential)
try await auth.currentUser?.delete()
}
func signIn(email: String, password: String) async throws {
try await auth.signIn(withEmail: email, password: password)
}
func signOut() throws {
try auth.signOut()
}
}
/// Auth.auth().createUserで新規ユーザーを作成した場合、Auth.auth().currentUserは新しいユーザーに置き換わる。
///
/// Authの初期化が完了する前にAuth.auth().currentUserを使うと、ログインしていたとしてもnilが返ってくる。
/// → addStateChangeListenerを使うとこの問題解決できる。
///
/// ログインしている状態でアプリを削除し、その後にアプリを再インストールした場合、なぜかログインされた状態から始まる。
/// → ログイン情報はキーチェーンに保存されてるらしい。
///
/// Firebase Consoleの方からログイン中のアカウントを削除しても、端末に何かしらの通信がいくわけではない模様。
///
/// Firebaseコンソールでユーザーをブロック、もしくは削除したときの情報の反映はどんな感じでやればいい?
XXX.hashValue
num.hashValue
とかurl.hashValue
とかは一旦アプリを終了すると、次は違う値になる。ソースはドキュメント。