2012-03-29 70 views
8

我正在编写一个聊天应用程序,并且正在更改我的数据库以使用核心数据。我目前直接使用sqlite,但我想利用iCloud功能,因此我正在切换引擎。设计使用核心数据的聊天应用程序

我的主要表被称为具有以下属性条目:

NSInteger type; 
NSDate* timestamp; 
NSString* username; 
NSString* session; 
NSString* body; 

其中“类型”可以是:

1 - message 
2 - file transfer (which then 'body' represents a file name in the documents folder) 
3 - user joined 
4 - user left 

我的应用也支持多用户聊天(因此为什么“用户加入'/'用户左''类型)。所有消息属于同一个会话(仅限多聊天),将具有有效的“会话”属性。

在我的聊天记录,我的问题是如何实现“负载更”像苹果的短信应用程序所做的:我会查询基于'username=%@ AND session IS NULL''session=%@'表明历史,并使用反向排序50 LIMIT“时间戳”。 然后我想要一个按钮“加载更多”,这将加载下50封邮件 - 我不知道如何使用核心数据。

我的下一个问题是如何显示对话列表。现在使用原始的sqlite,我对2个查询执行连接:第一个是每个用户的最后一条消息,第二个是每个多用户对话的最后一条消息。然后我按日期排列它们。 由于核心数据不支持连接,我不知道如何执行此查询。

谢谢

回答

14

有一个应用程序,做同样的事情,这里是我的见解。

首先,您应该在编码之前明智地考虑coredata和多线程。如果你需要帮助,请告诉我。

模型

您在Coredata实体,可以考虑像SQLite中的表,但在一个更抽象的方式。你应该为review Apple's documentation

我们可以在您的案例中找到至少三个不同的实体:用户,对话和消息。 (请注意最后一个,在导入SMS Framework时我遇到了一个名为Message的实体问题,您应该考虑在实体名称前加上..)

coredata的一个问题是,您无法直接存储数组(可能有一些未知类型),但无论如何。所以两种解决方案来存储用户:无论是在时,他们会用逗号和一个简单的正则表达式或拆分会给你的用户数量来界定的的NSString ..

所以你的模型可能看起来像:

Conversation{ 
    messages<-->>Message.conversation 
    lastMessage<-->Message.whateverName 
    //optional 
    users<<-->>User.conversation 
} 

Message{ 
    conversation<<-->Conversation.messages 
    whatevername<-->Conversation.lastmessage // "whatever as it does not really matter" 
} 

User{ 
    conversations<<-->>Conversation.users 
} 

会话必须与消息和消息具有多对多的关系才能与对话建立一对一的关系。

--edit

如果你想显示就像该消息应用程序(或我的应用程序)的对话的最后消息,您可以添加与消息一一对应关系。它不会在数据库/ coredata中存储两次消息。事实上,你创建一个coredata对象(在这种情况下是一条消息),并将其添加到对话中,内部发生的是对话存储该对象的coredata ID。为此消息添加一个关系(lastMessage)将仅存储另一个ID,而不是另一个对象。

编辑

用户--end略有不同,因为它们可以是多个对话(因为组群会话)的一部分,这就是为什么你需要一个多到许多关系的船。

您可以根据需要添加任意数量的属性,但这是最低要求!

  1. 实施

然后在你的代码,如果你想模仿iMessage的行为,这里是我做过什么:

第一控制器,在那里你可以看到所有的谈话

:使用NSFetchedResultController。查询应该只关于实体对话。

当单击一行时,我所做的是新视图具有会话对象和另一个NSFtechedResultController。然后我只查询实体消息,但用谓词指定我只想要这个对话。

如果你想检查我的应用程序,看流动性,go to this link.

编辑

  1. 代码片段找到对话的最后消息

请注意:这在找到一个更好的方法来做它之前(即使用提取的属性时)是临时答案

NSFetchRequest * req = [[NSFetchRequest alloc] init]; 
[req setEntity:[NSEntityDescription entityForName:@"Message" inManagedObjectContext:context]]; 
[req setPredicate:[NSPredicate predicateWithFormat:@"conversation == %@", self]]; /* did that from a Conversation object.. */ 
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"sent_date" ascending:NO]; 
[req setSortDescriptors:[NSArray arrayWithObject:sort]]; 

[sort release]; 
NSError * error = nil; 
NSArray * messages = [context executeFetchRequest:req error:&error]; 
[req release]; 
if ([messages count] > 0) { /* sanity check */ 
    return [messages objectAtIndex:0]; 
} 
return nil; 

编辑的--end

希望这有助于!

皮埃尔

+0

我按照你的建议去了,但是在阅读对话列表时 - 我怎样才能得到最后一条消息在那里显示(就像你在“消息”屏幕中做的那样)。谢谢。 – 2012-07-20 15:21:33

+0

嗨吉拉德 我稍微编辑了我的答案。 基本上,只需添加一个名为lastMessage的一对一关系或任何你想要的。 每当您收到新消息/ delete_the_last_one时,只需更新lastMessage字段即可。 如果你不是增加额外关系的粉丝,我可以猜测每次对话都会挖掘NSFetchRequest以查找最后一条消息,但这可能会非常糟糕,性能也很明智。 希望能帮助 – Pierre 2012-07-23 09:57:48

+0

非常感谢皮埃尔 - 这就是我最终做的。删除邮件时我有一个小问题(我需要手动识别最新邮件并更新我的会话对象),但至少我可以正确地获取历史记录。谢谢!!! – 2012-07-23 23:35:09

1

首先,你的心智模式是错的。您不应该将核心数据视为SQL数据库。是的,大多数情况下它使用SQL,但它仅仅是一个实现细节。你应该考虑对象图。

接下来,针对您的“50项”问题,查看NSFetchRequest。您可以告诉它从哪里开始(fetchOffset)以及要提取多少项(fetchLimit)。还有其他的选择。如果您的项目总数相对较少,那么您可以只读取整个数组(并且一次仅错误很多 - 请参阅fetchBatchSize)。

对于您的“连接”,请考虑对象如何相互关联,而不是数据库表连接。不幸的是,我不明白你试图用这部分问题来达到什么目的。但是,在形成谓词时,可以使用点符号模仿“连接”表。

编辑

当你创建一个会话对象,可以包括一个一对多的关系,以类似“参与者”,这将是一个集所有参与该对话的用户。反过来也是一个包含用户参与的所有对话的“用户”中的一对多关系(我假设你的数据库有多个用户?)。

因此,要获得所有在一个特定的用户参与,你可以不喜欢取的“参与者”与同类谓词的谈话,以“ALL participants.username =%@”

+0

对于问题的第二部分:我想要做的是列出所有会话(向他们展示的列表,用户可以在其中单击,然后扩大到看到完整的对话) 。它与iMessage类似。我想显示每个独特对话的最后一条消息。我不能通过用户名进行分组,因为用户可能会出现两次(一次在单个聊天中,一次在组聊天中) – 2012-05-08 15:55:55

+0

您想要显示所有对话的最后一条消息,或仅显示特殊用户是最后一条消息的消息?对不起,我不使用聊天客户端,所以我不熟悉这个比喻。 – 2012-05-08 18:51:00

+0

假设我与用户A(我们交换了100条消息)的对话,以及与用户B(50条消息)的另一对话和与用​​户A/B同时(10条消息)的组聊天:我想显示一个列表,其中包含3个项目(A,B,A/B)。如果您点击第一个条目 - 您将看到100条消息历史记录,点击第二条将显示50条消息历史记录,点击第三条将显示10条消息。我期待模仿iMessage。 – 2012-05-08 19:01:59