It is managed by CoreData:
I'm using the id of the object, but previously I was using a field "itemID" as a UUID, but it was the same behaviour.
It will be something like:
@objc(Item)
public class Item: NSManagedObject {
}
extension Item {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Item> {
return NSFetchRequest<Item>(entityName: "Item")
}
@NSManaged public var itemId: UUID?
@NSManaged public var name: String?
@NSManaged public var timestamp: Date?
}
extension Item : Identifiable {
}
I have added an extension to set the id, but it is still the same:
extension Item {
public var id: UUID { itemId ?? UUID() }
}
Thanks,
Post
Replies
Boosts
Views
Activity
Hello,
I implemented a solution that fits my requirements. If you are interested on it, here is the code: https://github.com/heltena/Orientation
Basically, I'm showing a sheet using UIKit, and controlling the supportedInterfaceOrientations in the UIViewController that is shown.
For lazy people, here is the code:
//
// RestrictedInterfaceOrientationSheet.swift
// Orientation
//
// Created by Heliodoro Tejedor Navarro on 1/3/23.
//
import SwiftUI
public enum ModalTransitionStyle: Int, @unchecked Sendable {
case coverVertical = 0
case flipHorizontal = 1
case crossDissolve = 2
fileprivate var uiKitVersion: UIModalTransitionStyle {
return UIModalTransitionStyle(rawValue: self.rawValue)!
}
}
struct RestrictedInterfaceOrientationSheet<SheetContent: View>: UIViewControllerRepresentable {
@Binding var isPresented: Bool
var restrictInterfaceOrientationTo: UIInterfaceOrientationMask
var modalTransitionStyle: ModalTransitionStyle
var animated: Bool
var content: () -> SheetContent
func makeUIViewController(context: Context) -> InternalViewController {
InternalViewController()
}
func updateUIViewController(_ uiViewController: InternalViewController, context: Context) {
if !isPresented {
uiViewController.presentedViewController?.dismiss(animated: animated)
return
}
if uiViewController.presentedViewController == nil {
let sheetViewController = PortraitHostingViewController(restrictInterfaceOrientationTo: restrictInterfaceOrientationTo, rootView: content())
sheetViewController.modalPresentationStyle = .overFullScreen // don't use .fullscreen, so it will remove the parent from the view hierarchy!
sheetViewController.modalTransitionStyle = modalTransitionStyle.uiKitVersion
uiViewController.present(sheetViewController, animated: animated)
return
}
if let presentedViewController = uiViewController.presentedViewController as? UIHostingController<SheetContent> {
presentedViewController.rootView = content()
return
}
fatalError("Invalid parent when dismissing a presented view controller")
}
class InternalViewController: UIViewController {
override func loadView() {
super.loadView()
view.backgroundColor = .clear
}
}
class PortraitHostingViewController<Content: View>: UIHostingController<Content> {
var restrictInterfaceOrientationTo: UIInterfaceOrientationMask
init(restrictInterfaceOrientationTo: UIInterfaceOrientationMask, rootView: Content) {
self.restrictInterfaceOrientationTo = restrictInterfaceOrientationTo
super.init(rootView: rootView)
}
@MainActor required dynamic init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
restrictInterfaceOrientationTo
}
}
}
struct RestrictedInterfaceOrientationSheetViewModifier<SheetContent: View>: ViewModifier {
@Binding var isPresented: Bool
var restrictInterfaceOrientationTo: UIInterfaceOrientationMask
var modalTransitionStyle: ModalTransitionStyle
var animated: Bool
@ViewBuilder var content: () -> SheetContent
func body(content: Content) -> some View {
content
.background {
RestrictedInterfaceOrientationSheet(
isPresented: $isPresented,
restrictInterfaceOrientationTo: restrictInterfaceOrientationTo,
modalTransitionStyle: modalTransitionStyle,
animated: animated,
content: self.content)
}
}
}
extension View {
public func sheet<SheetContent: View>(isPresented: Binding<Bool>, restrictInterfaceOrientationTo: UIInterfaceOrientationMask = .portrait, modalTransitionStyle: ModalTransitionStyle = .crossDissolve, animated: Bool = true, onDismiss: (() -> Void)?, @ViewBuilder content: @escaping () -> SheetContent) -> some View {
self.modifier(
RestrictedInterfaceOrientationSheetViewModifier(
isPresented: isPresented,
restrictInterfaceOrientationTo: restrictInterfaceOrientationTo,
modalTransitionStyle: modalTransitionStyle,
animated: animated,
content: content))
}
}
And how to use it:
struct ContentView: View {
@Binding var document: OrientationDocument
@State var showCamera = false
var body: some View {
Form {
Section {
Text(document.text)
} header: {
Text("Example")
}
}
.toolbar {
Button {
showCamera = true
} label: {
Label("Camera", systemImage: "camera")
}
}
.orientationLockModifier(mask: .portrait)
.sheet(isPresented: $showCamera, restrictInterfaceOrientationTo: .portrait, modalTransitionStyle: .crossDissolve, animated: false) {
print("bye bye")
} content: {
VStack(spacing: 20) {
Spacer()
Text("Show Camera Preview here")
Button {
showCamera = false
} label: {
Text("Close")
}
Spacer()
}
}
}
}
Before replacing the single to double quotes, check there are no double quotes inside the strings:
from: {'a': 'hello my "friend"!'}
to: {"a": "hello my "friend"!"} contains an error!
Is it possible to change the source of your json or run an script before generating it?
It looks like the json is coming from a call to a print function inside a python code (or similar language). If this is the case, try using the json package to dump the data in the correct format.
Best,
Thanks!
I have no idea how to solve your problem, but your code was soooo clear to solve mine (detecting the user events for a kiosk mode).
Thanks so much and I hope you can solve your problem, good luck!
Hello!
Yes, I'm using UIActivityViewController inside the ActivityView, which is a UIViewControllerRepresentable.
I did some tests and I realized that the video, for some reason I don't understand yet, is not correct to publish, not only in Whatsapp, but Twitter as well. Because I cannot publish the video using the Twitter website, I think the problem is not the sharing (UIActivityViewController), but the video itself.
The console:
Start the app, click "Generate and Share", and show the Share Sheet:
2021-08-12 15:35:33.728845-0500 TestingEarthtunesVideo[7630:3804560] [Warning] Warning once only: Detected a case where constraints ambiguously suggest a height of zero for a table view cell's content view. We're considering the collapse unintentional and using standard height instead. Cell: <SwiftUI.ListCoreCellHost: 0x15c80f000; baseClass = UITableViewCell; frame = (0 216.667; 343 44); clipsToBounds = YES; autoresize = W; layer = <CALayer: 0x281e8e5c0>>
TemperaryFolder: file:///private/var/mobile/Containers/Data/Application/C12414B3-3120-43B8-92E5-8533512219EC/tmp/C52ECFEA-A0F3-49AB-B00D-24D8DE4807E1
2021-08-12 15:35:37.234098-0500 TestingEarthtunesVideo[7630:3804790] AVDRegister - AppleAVD HEVC codec registered
2021-08-12 15:35:37.234374-0500 TestingEarthtunesVideo[7630:3804790] AVDRegister - AppleAVD H264 codec registered
2021-08-12 15:35:37.234516-0500 TestingEarthtunesVideo[7630:3804790] AVDRegister - AppleAVD Leghorn codec registered
2021-08-12 15:35:39.662574-0500 TestingEarthtunesVideo[7630:3804560] [User Defaults] Couldn't read values in CFPrefsPlistSource<0x2830dfa80> (Domain: com.apple.Sharing, User: kCFPreferencesCurrentUser, ByHost: No, Container: kCFPreferencesNoContainer, Contents Need Refresh: Yes): accessing preferences outside an application's container requires user-preference-read or file-read-data sandbox access
2021-08-12 15:35:39.663361-0500 TestingEarthtunesVideo[7630:3804560] [User Defaults] Couldn't read values in CFPrefsPlistSource<0x2830dfa80> (Domain: com.apple.Sharing, User: kCFPreferencesCurrentUser, ByHost: No, Container: kCFPreferencesNoContainer, Contents Need Refresh: Yes): accessing preferences outside an application's container requires user-preference-read or file-read-data sandbox access
2021-08-12 15:35:39.666559-0500 TestingEarthtunesVideo[7630:3804560] <CATransformLayer: 0x281e71f80> - changing property backgroundColor in transform-only layer, will have no effect
2021-08-12 15:35:39.790171-0500 TestingEarthtunesVideo[7630:3804560] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x283d82da0 _UIActivityActionCellTitleLabel:0x15d0db510.height >= 22.6667 (active)>",
"<NSLayoutConstraint:0x283d93cf0 V:|-(15)-[_UIActivityActionCellTitleLabel:0x15d0db510] (active, names: '|':UIView:0x15d0dc110 )>",
"<NSLayoutConstraint:0x283d93d40 V:[_UIActivityActionCellTitleLabel:0x15d0db510]-(15)-| (active, names: '|':UIView:0x15d0dc110 )>",
"<NSLayoutConstraint:0x283d65a90 'UIView-Encapsulated-Layout-Height' UIView:0x15d0dc110.height == 52 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x283d82da0 _UIActivityActionCellTitleLabel:0x15d0db510.height >= 22.6667 (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
2021-08-12 15:35:39.790711-0500 TestingEarthtunesVideo[7630:3804560] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x283d6de00 _UIActivityActionCellTitleLabel:0x15d0e5170.height >= 22.6667 (active)>",
"<NSLayoutConstraint:0x283d6d950 V:|-(15)-[_UIActivityActionCellTitleLabel:0x15d0e5170] (active, names: '|':UIView:0x15d0e5d70 )>",
"<NSLayoutConstraint:0x283d6d9a0 V:[_UIActivityActionCellTitleLabel:0x15d0e5170]-(15)-| (active, names: '|':UIView:0x15d0e5d70 )>",
"<NSLayoutConstraint:0x283d65e00 'UIView-Encapsulated-Layout-Height' UIView:0x15d0e5d70.height == 52 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x283d6de00 _UIActivityActionCellTitleLabel:0x15d0e5170.height >= 22.6667 (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
2021-08-12 15:36:08.766396-0500 TestingEarthtunesVideo[7630:3805356] [WindowServer] display_timer_callback: unexpected state (now:8beca030708 < expected:8beca09199d)
At this point, when I'm selecting Whatsapp and the group, and click the send button, the error is presented, but nothing appears in the console.
To show the process of the video creation, I did a new repo with the code I'm using to generate the video and a little explanation in the README.md (copy & paste):
This is a repo to explain the error when uploading a video to social networks (twitter and whatsapp).
Try to upload this video to your twitter account:
<< check the repo, I cannot post videos here >>
Error in Twitter:
Error in Whatsapp:
If I'm running in the terminal:
$ ffmpeg -i Media/earthtunes.mp4 Media/earthtunes-ffmpeg.mp4
It is possible to post the new video on Twitter... (probably it will work on Whatsapp as well)
I appreciate your feedback!!!
https://github.com/heltena/TestingEarthtunesVideo
Thanks!!!!!
I just removed the menu:
import SwiftUI
@main
struct FlowApp: App {
var body: some Scene {
DocumentGroup(viewing: FlowDocument.self) { file in
ContentView(document: file.$document)
}
.windowStyle(HiddenTitleBarWindowStyle())
.commands {
SidebarCommands()
CommandGroup(replacing: .saveItem) { } //// <<<<< HERE
}
}
}
Not sure if this is what you are looking for.
Also, added an error:
func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
throw CocoaError(.fileWriteNoPermission)
}
But, yes, I'm missing that the DocumentGroup does that internally.
I changed AVAudioPlayerNode by AVAudioSourceNode (check the BuildingASignalGenerator example).
My new code for your records:
import AVFoundation
import Combine
class ContentViewModel: ObservableObject {
		let audioEngine: AVAudioEngine
		var sourceNode: AVAudioSourceNode?
		let downMixer: AVAudioMixerNode
		let data: [Float]
		let sampleRate: Float
		
		init() {
				let sinFrequency: Float = 523.25	/* C5 */
				let sampleRate: Float = 44100
				let seconds: Float = 10
				
				let range = 0 ..< Int(seconds * sampleRate)
				self.data = range.map { sin(2.0 * .pi * Float($0) * sinFrequency / 44100) }
				self.sampleRate = sampleRate
				
				audioEngine = AVAudioEngine()
				downMixer = AVAudioMixerNode()
				
				let _ = audioEngine.mainMixerNode
				audioEngine.prepare()
				try! audioEngine.start()
				
				audioEngine.attach(downMixer)
				audioEngine.connect(downMixer, to: audioEngine.mainMixerNode, format: nil)
		}
	
		var isPlaying: Bool { sourceNode != nil }
		@Published private(set) var totalSeconds: Float = 0
		@Published private(set) var currentTime: Float = 0
		
		func stopCurrent() {
				if let sourceNode = sourceNode {
						audioEngine.detach(sourceNode)
						self.sourceNode = nil
				}
				self.totalSeconds = 0
				self.currentTime = 0
				self.objectWillChange.send()
		}
		
		func play(speedUpFactor: Float) {
				let playingSampleRate = sampleRate * speedUpFactor
				guard
						let dataFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: Double(playingSampleRate), channels: 1, interleaved: false)
				else {
						fatalError()
				}
				stopCurrent()
		
				self.totalSeconds = Float(data.count) / Float(playingSampleRate)
				self.currentTime = 0
				var index: Int = 0
				let sourceNode = AVAudioSourceNode(format: dataFormat) { [self] _, audioTimeStamp, frameCount, audioBufferList -> OSStatus in
						let ablPointer = UnsafeMutableAudioBufferListPointer(audioBufferList)
						
						let remainValues = min(Int(frameCount), data.count - index)
						for frame in 0 ..< remainValues {
								let value = index < data.count ? data[index] : 0.0
								index += 1
								
								for buffer in ablPointer {
										let buf: UnsafeMutableBufferPointer<Float> = UnsafeMutableBufferPointer(buffer)
										buf[frame] = value
								}
						}
						
						DispatchQueue.main.async {
								self.currentTime += Float(remainValues) / Float(playingSampleRate)
						}
						if remainValues < Int(frameCount) {
								for frame in remainValues ..< Int(frameCount) {
										for buffer in ablPointer {
												let buf: UnsafeMutableBufferPointer<Float> = UnsafeMutableBufferPointer(buffer)
												buf[frame] = 0.0
										}
								}
								DispatchQueue.main.async {
										stopCurrent()
								}
						}
						return noErr
				}
				
				self.sourceNode = sourceNode
				audioEngine.attach(sourceNode)
				audioEngine.connect(sourceNode, to: downMixer, format: nil)
				self.objectWillChange.send()
		}
}
And the SwiftUI view:
import SwiftUI
struct ContentView: View {
		@StateObject var viewModel = ContentViewModel()
		
		var body: some View {
				VStack {
						Spacer()
						HStack {
								Button("Play x1") {
										viewModel.play(speedUpFactor: 1)
								}
								Button("Play x2") {
										viewModel.play(speedUpFactor: 2)
								}
								Button("Stop") {
										viewModel.stopCurrent()
								}
						}
						if viewModel.isPlaying {
								Text("Playing: \(viewModel.currentTime) of \(viewModel.totalSeconds)")
						}
						Spacer()
				}
		}
}
struct ContentView_Previews: PreviewProvider {
		static var previews: some View {
				ContentView()
		}
}