The problem was related to the difference between Firebase/Core and FirebaseCore. The first is a subspec of the Firebase pod that depends on FirebaseAnalytics. The second is only the FirebaseCore pod. Only the latter should be used for macOS.
From ISO8601 spec, the problems are the representation and time zone
1 2 3 4 5 6 7
ISO 8601 = year-month-day time timezone For date and time, there are basic (YYYYMMDD, hhmmss, ...) and extended format (YYYY-MM-DD, hh:mm:ss, ...) Time zone can be Zulu, offset or GMT Separator for date and time can be space, or T There are week format for date, but it is rarely used Timezone can be a lot of spaces after Second is optional
Z: The ISO8601 basic format with hours, minutes and optional seconds fields. The format is equivalent to RFC 822 zone format (when optional seconds field is absent)
About locale Formatting Data Using the Locale Settings
Locales represent the formatting choices for a particular user, not the user’s preferred language. These are often the same but can be different. For example, a native English speaker who lives in Germany might select English as the language and Germany as the region
About en_US_POSIX Technical Q&A QA1480 NSDateFormatter and Internet Dates
On the other hand, if you’re working with fixed-format dates, you should first set the locale of the date formatter to something appropriate for your fixed format. In most cases the best locale to choose is “en_US_POSIX”, a locale that’s specifically designed to yield US English results regardless of both user and system preferences.
“en_US_POSIX” is also invariant in time (if the US, at some point in the future, changes the way it formats dates, “en_US” will change to reflect the new behaviour, but “en_US_POSIX” will not), and between machines (“en_US_POSIX” works the same on iOS as it does on OS X, and as it it does on other platforms).
The NSISO8601DateFormatter class generates and parses string representations of dates following the ISO 8601 standard. Use this class to create ISO 8601 representations of dates and create dates from text strings in ISO 8601 format.
For a snack bar or image viewing, it’s handy to be able to just flick or toss to dismiss
We can use UIKit Dynamic, which was introduced in iOS 7, to make this happen.
Use UIPanGestureRecognizer to drag view around, UISnapBehavior to make view snap back to center if velocity is low, and UIPushBehavior to throw view in the direction of the gesture.
NotificationCenter.default.addObserver(forName: UIScreen.didConnectNotification, object: nil, queue: nil) { (notification) in // Get the new screen information. let newScreen = notification.object as! UIScreen let screenDimensions = newScreen.bounds
// Configure a window for the screen. let newWindow = UIWindow(frame: screenDimensions) newWindow.screen = newScreen // Install a custom root view controller in the window. self.configureAuxilliaryInterface(with: newWindow)
// You must show the window explicitly. newWindow.isHidden = false // Save a reference to the window in a local array. self.additionalWindows.append(newWindow)}
funcapplication(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { // Called when a new scene session is being created. // Use this method to select a configuration to create the new scene with.
print(UIApplication.shared.connectedScenes) let scene = UIWindowScene(session: connectingSceneSession, connectionOptions: options)
// Configure a window for the screen. self.newWindow = UIWindow(frame: CGRect(x: 0, y: 0, width: 1000, height: 500)) // self.newWindow.backgroundColor = UIColor.yellow // Install a custom root view controller in the window.
let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "other") as! OtherViewController self.newWindow.rootViewController = viewController self.newWindow.windowScene = scene
// You must show the window explicitly. self.newWindow.isHidden = false
Use Auto Layout and basic UIView animation. Use debouncer to avoid hide gets called for the new show. Use debouncer instead of DispatchQueue.main.asyncAfter because it can cancel the previous DispatchWorkItem
If we add this error message on UIView in ViewController and we use KeyboardHandler to scroll the entire view, then this snack bar will move up as well
completion A block object to be executed when the animation sequence ends. This block has no return value and takes a single Boolean argument that indicates whether or not the animations actually finished before the completion handler was called. If the duration of the animation is 0, this block is performed at the beginning of the next run loop cycle. This parameter may be NULL.
If there are lots of logics and states inside a screen, it is best to introduce parent and child container, and switch child depends on state. Each child acts as a State handler.
In less logic case, we can introduce a Scenario class that holds the state. So the ViewController can be very slim. The thing with State is that all possible scenarios are clear and required to be handled
finalclassUserDetailScenario{ enumState{ case unknown case getUser(Email) case newUser(Email) case existingUser(User) case failure(Error) } var state: State = .unknown(nil) { didSet { self.reload() } } privatefuncreload() { switch state { case .unknown: handleUnknown() case .getUser(let email): handleGetUser(email: email) case .newUser(let email): handleNewUser(email: email) case .existingUser(let user): handleExistingUser(user: user case .failure(let error): logError(error) } }
To add custom content to UIAlertController, there are some workarounds
Add content onto UITextField
Restyle UITextField and add custom content
Subclass UIAlertController and handle UI in viewWillAppear
By subclassing we can tweak the whole view hierarchy and listen to events like layout subviews, but this can be very fragile.
Make custom UIViewController that looks like UIAlertController
This is the correct way but takes too much time to imitate UIAlertController, and have to deal with UIVisualEffectView, resize view for different screen sizes and dark mode
Instead, the notifications from your app will automatically start getting delivered.
Notifications that are delivered with provisional authorization will have a prompt like this on the notification itself. And this will help the users decide after having received a few notifications whether they want to keep getting these notifications or whether they want to turn them off
It’s an automatic trial of the notifications from your app to help your users make a more informed decision about whether they want these notifications.
Provisional Authorization takes advantage of another new feature in iOS 12: the ability for messages to be “delivered quietly.” When a notification is delivered quietly, it can only be seen in the iOS Notification Center, which the user accesses by swiping down from the top of their phone. They don’t appear as banners or show up on the lock screen. As you might have guessed, quiet notifications also don’t make a sound.
If a user taps the “Keep” button, they can decide whether they want your app’s notifications to start getting delivered prominently (i.e. fully opt-in to push notifications) or continue to receive them quietly (i.e. pushes continue to get sent directly to the Notification Center).
The intent of Provisional Authorization is to give users a trial run with your app’s notifications. Apple created Provisional Authorization because it realized that it’s impossible for users to make an informed choice about whether or not they want to receive push notifications from an app until they’ve seen what kinds of messages the app is going to send them.
There are times we want to log if user can receive push notification. We may be tempted to merely use isRegisteredForRemoteNotifications but that is not enough. From a user ‘s point of view, they can either receive push notification or not. But behind the scene, many factors are in the game. It can be that user has disabled push notification in app settings or in iOS Settings. It can also be that user enables push notification but disables all sound or banner display mechanism.
isRegisteredForRemoteNotifications is that your app has connected to APNS and get device token, this can be for silent push notification currentUserNotificationSettings is for user permissions, without this, there is no alert, banner or sound push notification delivered to the app Here is the check
For iOS 10, with the introduction of UserNotifications framework, instead of checking for currentUserNotificationSettings, you should use UserNotifications framework
1 2 3 4 5 6 7 8 9 10
center.getNotificationSettings(completionHandler: { settings in switch settings.authorizationStatus { case .authorized, .provisional: print("authorized") case .denied: print("denied") case .notDetermined: print("not determined, ask user for permission now") } })
Push notification can be delivered to our apps in many ways, and we can ask for that
DiscussionThe AnyHashable type forwards equality comparisons and hashing operations to an underlying hashable value, hiding its specific underlying type.You can store mixed-type keys in dictionaries and other collections that require Hashable conformance by wrapping mixed-type keys in AnyHashable instances
1 2 3 4 5 6 7 8 9 10
let descriptions: [AnyHashable: Any] = [ AnyHashable("😄"): "emoji", AnyHashable(42): "an Int", AnyHashable(Int8(43)): "an Int8", AnyHashable(Set(["a", "b"])): "a set of strings" ] print(descriptions[AnyHashable(42)]!) // prints "an Int" print(descriptions[AnyHashable(43)]) // prints "nil" print(descriptions[AnyHashable(Int8(43))]!) // prints "an Int8" print(descriptions[AnyHashable(Set(["a", "b"]))]!) // prints "a set of strings"
We don’t necessarily need to map from [AnyHashable: Any] to [String: Any], we can just access via string key
/// The content of the scroll view. publicvar content: Content
}
1 2 3 4 5 6 7 8 9 10
extensionGroup : ViewwhereContent : View{
/// The type of view representing the body of this view. /// /// When you create a custom view, Swift infers this type from your /// implementation of the required `body` property. publictypealiasBody = Never
If this property is nil, all valid source files in the target’s path will be included and specified paths are relative to the target path.
A path can be a path to a directory or an individual source file. In case of a directory, the Swift Package Manager searches for valid source files recursively inside it.